【问题标题】:Decorators on bound methods with access to class and his ancestors可以访问类及其祖先的绑定方法上的装饰器
【发布时间】:2017-02-03 12:20:37
【问题描述】:

当我在 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 字段。

有没有办法在装饰器中得到它?

【问题讨论】:

    标签: python class reflection decorator python-decorators


    【解决方案1】:

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

    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()
    

    【讨论】:

      【解决方案2】:

      函数修饰发生在执行类主体时,此时对类本身或其基类一无所知。这意味着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 之前,这只能通过类装饰器或元类实现; each 在类主体创建后调用,您将可以访问(至少)基类。

      使用类装饰器,例如:

      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__ method,它也可以为您提供访问权限;每次创建当前类的子类时都会调用 (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
      

      【讨论】:

        【解决方案3】:

        并不是说传递给装饰器包装器的第一个 arg 将是一个对象实例 (self)。多亏了它,您可以访问 w=所有您需要的东西。

        查看拉斐尔的回应: Related issue

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

        self 是第一个参数

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-01-22
          • 1970-01-01
          • 2019-03-07
          • 2011-06-26
          • 2018-05-19
          • 1970-01-01
          • 2020-02-05
          • 2017-05-04
          相关资源
          最近更新 更多