这个Python装饰器是如何工作的?
编辑/澄清以使我的问题更具体: *我能看到装饰器静态日志函数是如何被调用的,但我不明白_是怎么被调用的,以及它的结果是如何成为日志的结果的。我能看到入口/进入的东西是怎么工作的*
class logger:
@staticmethod
def log(func):
def ___(*args, **kwargs):
try:
print "Entering: [%s] with parameters %s" % (func.__name__, args)
try:
return func(*args, **kwargs)
except Exception, e:
print 'Exception in %s : %s' % (func.__name__, e)
finally:
print "Exiting: [%s]" % func.__name__
return ___
class x:
@logger.log
def first_x_method(self):
print 'doing first_x_method stuff...'
x().first_x_method()
输出是:
Entering: [first_x_method] with parameters (<__main__.x instance at 0x0000000001F45648>,)
doing first_x_method stuff...
Exiting: [first_x_method]
我知道logger是一个类,它有一个静态方法,用来装饰(@logger.log
)第一个方法(first_x_method)。
不过我不明白为什么___
这个子方法(它可以是任何名字)会被调用。
3 个回答
def decorate(fn):
def wrapper():
fn()
print 'Wrapping function ', wrapper
return wrapper
def x():
pass
print 'Original x ', x
x = decorate(x)
print 'New x ', x
输出:
Original x <function x at 0x7f3c51e9a758>
Wrapping function <function wrapper at 0x7f3c51e9a7d0>
New x <function wrapper at 0x7f3c51e9a7d0>
注意现在 x
和 wrapper
是一样的。当你调用 x
时,其实是在调用 wrapper
。
不要把这个回答标记为正确答案。请把其他的某个回答标记为正确答案。这样做的目的是为了纠正一个误解,这样你才能更好地理解其他的回答。
装饰器是一种函数,它可以接收另一个函数作为参数,并返回一个“装饰过的”函数,这个函数在被调用时会实际执行。
在这个例子中,logger.log
接收了函数 first_x_method
。实际的装饰器实现会创建一个函数,这个函数在运行它的参数之前和之后会打印一条消息,并返回这个函数(在这里叫做 ____
)。所以实际上,每次你调用 first_x_method
时,你实际上是在调用 ____
,而 ____
会保持对 first_x_method
的引用,称为 func
。
换句话说,____
“捕获”了 first_x_method
并替换了它。每当它被调用时,首先会打印一些内容,然后调用它所引用的函数,最后再打印一些内容。
编辑:也许这个简单的例子可以帮助你理解:
def decorate(func):
print "Decorate called"
def internal(*args, **kwargs):
print "Calling internal"
func(*args, **kwargs)
print "Original function returned: "
print "Created the decorated function, returning."
return internal
@decorate
def foo(s):
print "foo called with: ", s
return 42
foo('hello')
关于装饰器的基本事实是
@decorator
def func(): ...
和
def func(): ...
func=decorator(func)
是完全一样的。
所以,
@logger.log
def first_x_method(self): ...
和
def first_x_method(self): ...
first_x_method=logger.log(first_x_method)
是相同的,这样 logger.log
这个静态方法就会用参数 func = first_x_method
被调用。
在调用 logger.log(first_x_method)
的过程中,定义并返回了一个子方法 __
。
因此,first_x_method=logger.log(first_x_method)
这行代码就把 first_x_method
设置为指向这个子方法 __
。
在 first_x_method()
中的括号告诉 Python 要调用这个方法 first_x_method
。
所以 x().first_x_method()
首先创建了一个类 x 的实例,然后调用了方法 first_x_method
(并把 x() 作为第一个参数传进去)。
因为 first_x_method
指向的是 __
,所以实际上调用的是 __
。