python decorator修改当前范围内的变量

时间:2010-04-26 14:29:19

标签: python scope decorator

目标:制作一个可以修改其使用范围的装饰器。

如果有效:

class Blah(): # or perhaps class Blah(ParentClassWhichMakesThisPossible)

    def one(self):
        pass

    @decorated
    def two(self):
        pass

>>> Blah.decorated
["two"]

为什么呢?我本质上想要编写可以维护特定字典方法的类,这样我就可以在每个类的基础上检索不同类型的可用方法的列表。 errr .....

我想这样做:

class RuleClass(ParentClass):
    @rule
    def blah(self):
        pass

    @rule
    def kapow(self):
        pass

    def shazam(self):

class OtherRuleClass(ParentClass):
    @rule
    def foo(self):
        pass

    def bar(self):
        pass

>>> RuleClass.rules.keys()
["blah", "kapow"]
>>> OtherRuleClass.rules.keys()
["foo"]

2 个答案:

答案 0 :(得分:9)

您可以使用类装饰器(在Python 2.6中)或元类来执行您想要的操作。类装饰器版本:

def rule(f):
    f.rule = True
    return f

def getRules(cls):
    cls.rules = {}
    for attr, value in cls.__dict__.iteritems():
        if getattr(value, 'rule', False):
            cls.rules[attr] = value
    return cls

@getRules
class RuleClass:
    @rule
    def foo(self):
        pass

元类版本将是:

def rule(f):
    f.rule = True
    return f

class RuleType(type):
    def __init__(self, name, bases, attrs):
        self.rules = {}
        for attr, value in attrs.iteritems():
            if getattr(value, 'rule', False):
                self.rules[attr] = value
        super(RuleType, self).__init__(name, bases, attrs)

class RuleBase(object):
    __metaclass__ = RuleType

class RuleClass(RuleBase):
    @rule
    def foo(self):
        pass

请注意,这些都不符合您的要求(修改调用命名空间),因为它很脆弱,很难并且通常是不可能的。相反,它们都通过检查所有属性并填充__init__属性来对类进行后处理 - 通过类装饰器或元类的rules方法。两者之间的区别在于元类解决方案在Python 2.5及更早版本(低至2.2)中工作,并且元类是继承的。使用装饰器,子类必须各自单独应用装饰器(如果他们想要设置规则属性。)

两个解决方案都不考虑继承 - 在查找标记为规则的方法时,它们不会查看父类,也不会查看父类rules属性。如果那是你想要的,那么要扩展它们并不难。

答案 1 :(得分:4)

问题是,在调用decorated装饰器时, 还没有对象Blah:在之后构建了类对象类体完成执行。最简单的方法是让decorated将信息存储在“其他地方”,例如一个函数属性,然后最终传递(类装饰器或元类)将该信息收集到您想要的字典中。

类装饰器更简单,但它们不会被继承(所以它们不会来自父类),而元类继承 - 所以如果你坚持继承,那么元类它必须是。最简单的一点,有一个类装饰器和你在Q开头的“列表”变体,而不是你后来的“dict”变体:

import inspect

def classdecorator(aclass):
  decorated = []
  for name, value in inspect.getmembers(aclass, inspect.ismethod):
    if hasattr(value, '_decorated'):
      decorated.append(name)
      del value._decorated
  aclass.decorated = decorated
  return aclass

def decorated(afun):
  afun._decorated = True
  return afun

现在,

@classdecorator
class Blah(object):

    def one(self):
        pass

    @decorated
    def two(self):
        pass

为您提供您在Q的第一部分中请求的Blah.decorated列表。建立一个字典,正如您在Q的第二部分中所要求的,只是意味着将decorated.append(name)更改为{{1在上面的代码中,当然将类装饰器中的decorated[name] = value初始化为空的dict而不是空列表。

元类的变体将使用元类的decorated在构建类主体后执行基本相同的后处理 - 元类的__init__获取对应于类主体的dict作为其最后一个参数(但你必须通过适当处理任何基类的类似字典或列表来自己支持继承)。因此,元类方法在实践中仅比“类装饰器”更“复杂”,但从概念上讲,大多数人认为它更加困难。如果你需要,我会给出元类的所有细节,但如果可行,我建议坚持使用更简单的类装饰器。