【问题标题】:Python decorator which set class variable设置类变量的 Python 装饰器
【发布时间】:2011-07-17 14:41:53
【问题描述】:

我有一个代码可以获取FooBar 中所有函数的列表以及该函数在其参数消息中支持的正则表达式:

functionList = []

def notify(RegExpression):
    def _notify(function):
        functionList.append((RegExpression, function))

        return function

    return _notify

class FooBar:
    @notify(".*")
    def everything(self, message):
        pass

        @notify("(\w+):.*")
    def reply(self, message):
        pass

for foo in functionList:
    print("%s => %s" % foo)

我想做类似的事情,但将函数列表及其参数作为类变量放入类中。当存在更多像FooBar 这样的类时,它将防止出现问题。每个类都应该有自己的列表。

def notify(RegExpression):
    # ???

class FooBar:
    functionList = []

    @notify(".*")
    def everything(self, message):
        pass

        @notify("(\w+):.*")
    def reply(self, message):
        pass

for foo in FooBar.functionList:
    print("%s => %s" % foo)

notify() 里面放了什么?

【问题讨论】:

    标签: python class decorator


    【解决方案1】:

    直接使用函数装饰器是不可能的,因为您需要访问当前正在定义的类,而这个类还不存在。一种解决方案是让装饰器仅将正则表达式存储为方法的属性,并具有在基类上收集这些方法的功能:

    def notify(regex):
        def decorate(func):
            func.regex = regex
            return func
        return decorate
    
    class Baz(object):
        @property
        def function_list(self):
            for attr in dir(self):
                obj = getattr(self, attr)
                if callable(obj) and hasattr(obj, "regex"):
                    yield obj
    
    class FooBar(Baz):
        @notify(".*")
        def everything(self, message):
            pass
    
        @notify("(\w+):.*")
        def reply(self, message):
            pass
    
    for foo in FooBar().function_list:
        print("%s => %s" % (foo.regex, foo))
    

    【讨论】:

    • 嘿,我们都发布了类似的解决方案,相隔几秒钟。该物业是个好主意,我没有想到。
    • @Jochen:嗯,除了时间上的巧合之外。基本上有三个选项可以做到这一点:通过基类、通过类装饰器或通过元类。我们的解决方案反映了这三个中最简单的两个选项:)
    • 我不知道可以在这样的方法上设置属性。谢谢!
    【解决方案2】:

    notify 被调用时,Foobar 类甚至还不存在。因此,您不能只使用装饰器。

    您可以做的是用装饰器标记函数并在定义类后收集它们。您可以使用元类或使用这样的类装饰器来做到这一点:

    import inspect
    def notify(regex):
        def mark( func ):
            func.regex = regex
            return func
        return mark
    
    def collect( cls ):
        cls.functionList=[]
        for name, func in inspect.getmembers(cls, inspect.ismethod):
            if hasattr(func, 'regex'):
                cls.functionList.append(func)
        return cls
    
    @collect
    class FooBar(object):
    
        @notify(".*")
        def everything(self, message):
            pass
    
        @notify("(\w+):.*")
        def reply(self, message):
            pass
    
    for foo in FooBar.functionList:
         print("%s => %s" % (foo.regex, foo))
    

    【讨论】:

      【解决方案3】:

      我还是写了它,所以我可能只是发布第三个选项。它使用元类来收集函数:

      def notify(regex):
          def mark( func ):
              func.regex = regex
              return func
          return mark
      
      class RegexBase(object):
          class __metaclass__(type):
              """ creates a list of functions with a `regex` attribute 
                  and stores it on the class as `functionList`
              """
              def __new__(cls, name, bases, attr):
                  fl = []
                  for obj in attr.itervalues():
                          if hasattr(obj, 'regex'):
                              fl.append(obj)
                  attr['functionList'] = fl
                  return type.__new__(cls, name, bases, attr)
      
      
      class FooBar(RegexBase):
      
          @notify(".*")
          def everything(self, message):
              pass
      
          @notify("(\w+):.*")
          def reply(self, message):
              pass
      
      for foo in FooBar.functionList:
           print("%s => %s" % (foo.regex, foo))
      

      我想将所有选项放在一个线程中很好。

      【讨论】:

      • 所以这是第三个选项。好的! :)
      【解决方案4】:

      这是针对 Python 3 更新的 Jochen Ritzel's 答案。

      def notify(regex):
          def mark( func ):
              func.regex = regex
              return func
          return mark
      
      
      class Meta(type):
          """ creates a list of functions with a `regex` attribute 
              and stores it on the class as `functionList`
          """
          def __new__(cls, name, bases, attr):
              fl = []
              for obj in attr.values():
                      if hasattr(obj, 'regex'):
                          fl.append(obj)
              attr['functionList'] = fl
              return type.__new__(cls, name, bases, attr)
      
      class RegexBase(object, metaclass=Meta):
        pass
      
      
      class FooBar(RegexBase):
          @notify(".*")
          def everything(self, message):
              pass
      
          @notify("(\w+):.*")
          def reply(self, message):
              pass
      
      
      for foo in FooBar.functionList:
           print("%s => %s" % (foo.regex, foo))
      

      【讨论】:

        猜你喜欢
        • 2012-08-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-11-24
        • 2020-10-09
        • 2023-03-23
        • 1970-01-01
        • 2019-06-11
        相关资源
        最近更新 更多