设置类变量的Python装饰器

时间:2011-03-17 17:42:30

标签: python class decorator

我有一个代码,它获取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()的内容是什么?

4 个答案:

答案 0 :(得分:4)

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

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

答案 1 :(得分:3)

当调用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))

答案 2 :(得分: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))

我想在一个帖子中将所有选项放在一起很好。

答案 3 :(得分:0)

此处为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))