Python装饰器,用于设置类变量

4

我有一段代码,可以获取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个回答

5

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

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 Ritzel
1
@Jochen:嗯,除了时间上的巧合之外,这并不是太巧合。基本上有三种选项可以做到这一点:通过基类、通过类装饰器或通过元类。我们的解决方案反映了这三个选项中最容易的两个选项 :) - Sven Marnach
我从来不知道可以像那样在方法上设置属性。谢谢! - Mike Conigliaro

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

4

我还是写了这个选项,所以我最好把第三个选项发布出来。它使用元类来收集函数:

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

我想把所有选项都放在一个线程里,这样很方便。


那么这就是第三个选项。不错! :) - Sven Marnach

2

这是Jochen Ritzel的答案,已更新至Python 3。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接