Py学习  »  Python

使用Python装饰器收集可用方法的信息,并将该数据存储在类或实例中

Alberto • 4 年前 • 1599 次点击  

我发现了很多关于装饰器和类的问题,以及访问正在装饰的类实例的问题。据我所知,装饰生成器无法访问对象实例,因为当时还没有创建任何对象。

尽管如此,如果装饰者使用一个类引用(我的意思是,接收cls),那么我可以访问一些类字段:

     class Foo:
          _tags = {}
      
          @classmethod
          def _tag(cls, tag_name):
              def decorator(func):
                  def tmp(*args, **kwargs):
                      return func(*args, **kwargs)
     
                  cls._tags[tag_name] = func.__name__
                  return tmp
     
              return decorator

这是子类的魅力所在,我可以写:

 @Foo._tag("something")
 def somethingelse():
     pass

但这将信息存储在父类中(正如预期的那样),因为我们正在讨论类变量。

我想知道是否有一种方法可以实现类似的行为,但要保证每个子类都有自己的 _tags 所有物

我可以预见一些变通方法,比如在测试过程中解析源代码或检查函数 __init__ .但我在寻找一种更简单的方法,如果有这种方法的话。

编辑1: 为了更清楚,我想从子类修饰方法。因此,这是不可能的:

     Class Bar (Foo):
        @Bar._tag("method")
        def method(self):
           pass

因为处理器/编译器尚未完成类的创建。否则,提出的带有类/方法的两级字典的解决方案对我来说就足够了。

变通方法

我的装饰师:

    @classmethod
    def TAG(cls, name):
        def decorator(func):
            def tmp(*args, **kwargs):
                return func(*args, **kwargs)

            tmp._has_alias = name
            return tmp
        return decorator

在期间 __初始化__ ,我得到了这些信息,并存储:

        self._tags = {getattr(self, name).has_alias: name for name in dir(self) if callable(getattr(self, name)) and hasattr(getattr(self, name), "_has_alias")}
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/133241
文章 [ 1 ]  |  最新文章 4 年前
Ajax1234
Reply   •   1 楼
Ajax1234    4 年前

一个快速的解决方法可能是治疗 _tags 作为字典的字典,顶级键是子类的名称,其关联字典包含由继承的 _tag :

from collections import defaultdict
class Foo:
  _tags = defaultdict(dict)
  @classmethod
  def _tag(cls, tag_name):
    def decorator(func):
      def tmp(*args, **kwargs):
         return func(*args, **kwargs)
      cls._tags[cls.__name__][tag_name] = func.__name__ #storing tag_name under the name of the subclass
      return tmp
    return decorator

现在,在创建子类和包装函数时, _标签 将在子类名称下存储值 _标签 被称为:

class A(Foo):
  pass

class B(Foo):
  pass

@A._tag('test_a')
def a():
  pass

@B._tag('test_b')
def b():
  pass

print(dict(Foo._tags))

输出:

{'A': {'test_a': 'a'}, 'B': {'test_b': 'b'}}