如何装饰可调用类的实例?
def decorator(fn):
def wrapper(*args, **kwargs):
print 'With sour cream and chives!',
return fn(*args, **kwargs)
return wrapper
class Potato(object):
def __call__(self):
print 'Potato @ {} called'.format(id(self))
spud = Potato()
fancy_spud = decorator(Potato())
这段代码展示了两个可调用的类实例,一个是用装饰器装饰过的,另一个是普通的:
>>> spud()
Potato @ 140408136280592 called
>>> fancy_spud()
With sour cream and chives! Potato @ 140408134310864 called
我在想,是否可以只对某一个实例使用@decorator
这种语法,而不是对整个类或方法进行装饰,这样会影响到每一个实例。根据这个流行的回答,@语法其实只是个简化写法:
function = decorator(function)
但这样说是不是太简单化了?我尝试了很多方法,发现只有在def
、class
、空格或者@another_decorator
之前使用这种语法才有效。
@decorator
baked = Potato()
这是一个SyntaxError
错误。
baked = Potato()
@decorator
baked
这也是SyntaxError
错误。
@decorator
def baked(_spud=Potato()):
return _spud()
这样写可以,但看起来很丑,而且有点像是在作弊。
2 个回答
是的,这个说法有点过于简单化了。如果我们看看语法规则,会发现decorator
这个词只出现在decorators
的规则里,而这个规则只在classdef
(类定义)或funcdef
(函数定义)中出现:
decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
decorators: decorator+
decorated: decorators (classdef | funcdef)
语言参考文档中提到的内容(我觉得这也是链接回答中重复的内容)是
@f1(arg)
@f2
def func(): pass
等同于
def func(): pass
func = f1(arg)(f2(func))
对于类定义也是类似的。但是,这并不意味着@decorator
的语法可以随便用;它只能在函数或类定义的前面使用。
顺便提一下,甚至官方文档也不是完全正确的;在调用装饰器的时候,函数(或类)并没有绑定到外部的命名空间或作用域中,所以这些语法并不完全等价。
关于def
和class
语句,有一些有趣的地方,我认为这也是它们是唯一支持@decorator
语法的原因之一:它们是Python中唯一可以将名称绑定到一个“知道这个名称是什么”的对象上的方式。
最后,这里还有另一种你可能喜欢的调用装饰器的方法:
@decorator
class baked:
__metaclass__ = lambda *_: Potato()
你的问题是:
根据这个热门的回答,@语法其实就是一个简化写法,等同于:
function = decorator(function)
不过,更准确的说法是:
@decorator
def function():
pass
其实是另一种简化写法:
def function():
pass
function = decorator(function)
装饰器是专门用来装饰函数、方法或类的定义的。关于类装饰器的介绍,可以参考这个PEP文档,里面描述了语法规则:
decorated: decorators (classdef | funcdef)
funcdef: 'def' NAME parameters ['->' test] ':' suite
正如你所看到的,装饰器必须紧接在classdef
或funcdef
之前,所以不能直接在可调用类的实例上使用它。