如何装饰可调用类的实例?

时间:2014-06-09 14:29:05

标签: python decorator

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

我想知道是否支持在一个实例的callable上使用@decorator语法 - 而不是装饰适用于每个实例的类/方法。根据{{​​3}}流行的答案,@syntax只是糖:

function = decorator(function)

但这是一种过度简化吗?我的所有半生不熟的尝试似乎只有在defclass,空格或@another_decorator之前语法出现时才有效。

@decorator
baked = Potato()

那是SyntaxError

baked = Potato()
@decorator
baked

另外SyntaxError

@decorator
def baked(_spud=Potato()):
    return _spud()

工作,但是丑陋而且有点作弊。

2 个答案:

答案 0 :(得分:4)

是的,这过于简单化了。如果我们查看the grammar,则decorator仅显示在decorators规则中,该规则仅作为classdeffuncdef的一部分显示:

decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
decorators: decorator+
decorated: decorators (classdef | funcdef)

language reference says(我认为这是在链接答案中重复的内容)是

@f1(arg)
@f2
def func(): pass

相当于

def func(): pass
func = f1(arg)(f2(func))

类似于类定义。但这并不意味着@decorator语法可以应用于任何地方;它只在函数或类定义之前有效。

顺便说一句,即使官方文件也不严格正确;在调用decorator时,函数(或类)不会绑定到封闭的命名空间或作用域中,因此给定的语法并不完全等效。

defclass语句有一些有趣的内容,我认为这是@decorator语法支持的唯一语句的一部分原因:它们是Python中将名称绑定到知道该名称是什么的对象的唯一方法。

最后,这是另一种调用您可能喜欢的装饰器的方法:

@decorator
class baked:
    __metaclass__ = lambda *_: Potato()

答案 1 :(得分:3)

你质疑:

  

根据这个流行的答案,@syntax只是糖:

     

function = decorator(function)

然而,更准确地说

@decorator
def function():
    pass

是语法糖:

def function():
    pass
function = decorator(function)

装饰器专门用于装饰函数,方法或类 definitions PEP that introduced class decorators描述了语法:

decorated: decorators (classdef | funcdef)

funcdef: 'def' NAME parameters ['->' test] ':' suite

如您所见,装饰器必须紧接在classdeffuncdef之前,因此无法直接在可调用类的实例上使用它。