目标:制作一个可以修改其使用范围的装饰器。
如果有效:
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"]
答案 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作为其最后一个参数(但你必须通过适当处理任何基类的类似字典或列表来自己支持继承)。因此,元类方法在实践中仅比“类装饰器”更“复杂”,但从概念上讲,大多数人认为它更加困难。如果你需要,我会给出元类的所有细节,但如果可行,我建议坚持使用更简单的类装饰器。