可以访问类及其祖先的绑定方法上的装饰符



当我在Python类中装饰绑定方法时,我需要从外部类中获得这个装饰器中的一些信息。这可能吗?

例如:

def modifier(func):
    import sys
    cls_namespace = sys._getframe(1).f_locals
    cls_namespace['data']  # dictonary has no key 'data'
    ...
    return func
class Parent:
    data = "Hi!"
class Child(Parent):
    @modifier
    def method(self):
        pass

cls_namespace只是当前类的不完整命名空间,没有我需要获得的data字段。

有办法得到它的装饰?

函数修饰发生在执行类主体时,此时对类本身或其基类一无所知。这意味着modifier修饰了未绑定的函数对象,只有在实例上实际调用func时,它才会被绑定。

你可以返回一个包装函数来代替装饰函数,它将被绑定,你可以访问self:

from functools import wraps
def modifier(func):
    @wraps(func)
    def wrapper(self, *args, **kwargs):
        # self is an instance of the class
        self.data
        return func(self, *args, **kwargs)
    return wrapper

每次在实例上调用method时,都会调用包装器。

如果必须在类创建时访问基类,则必须等到class Child语句完成执行。在Python 3.6之前,这只能通过类装饰器或元类实现;每个类都是在类体创建后调用的,您至少可以访问基类。

使用类装饰器,例如:

def modifier(cls):
    # cls is the newly created class object, including class attributes
    cls.data
    return cls
@modifier
class Child(Parent):
    def method(self):
        pass

注意现在装饰器的位置。

Python 3.6增加了一个__init_subclass__方法,也可以给你这样的访问权限;(class)方法在每次创建当前类的子类时调用:

class Parent:
    def __init_subclass__(cls, **kwargs):
        # cls is a subclass of Parent
        super().__init_subclass__(**kwargs)
        cls.data
    data = "Hi!"
class Child(Parent):
    def method(self):
        pass

这是一个特别均匀的解决方案:

def wrap_class(cls):
    """Wrap a class to allow binding all decorated methods."""
    for func in cls.__dict__.values():
        if hasattr(func, '__wrapped__'):
            func.__wrapped__.im_class = cls
    return cls

要求您使用@functools.wraps(func),然后用@wraps_class来装饰类。

装饰的例子:

def decorator(func):
    @wraps(func)
    def inner(self, *args, **kwargs):
        cls = self.__class__  # bound and straight-forward
        ...
    def extra(*args, **kwargs):
        cls = func.im_class  # Not bound
        ...
    inner.extra = extra
    return inner

使用例子:

@wrap_class
class Badger:
    @decorator
    def stoat(self, mushroom, snake):
        pass
Badger().stoat()
Badger.stoat.extra()

传递给装饰器包装器的第一个参数将不是对象实例(self)。多亏了它,你可以访问w=你需要的一切。

见拉斐尔的回应:相关问题

def wrapped(self, *f_args, **f_kwargs):

self是第一个参数

相关内容

  • 没有找到相关文章

最新更新