How does this python decorator work?
Edit/Clarification to make my question specific to my query: *I can see how the decorator static log function is called but I don't see how _ is called and how the result of it is the result of log. I cam see how the entry/enter stuff works*
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()
gives this output:
Entering: [first_x_method] with parameters (<__main__.x instance at 0x0000000001F45648>,)
doing first_x_method stuff...
Exiting: [first_x_method]
I can开发者_运维问答 see that logger is a class with a static method that is used to decorate (@logger.log
) first_x_method.
However I don't understand why the ___
sub method (and it can be any name) is called.
The fundamental fact about decorators is that
@decorator
def func(): ...
is exactly equivalent to
def func(): ...
func=decorator(func)
So,
@logger.log
def first_x_method(self): ...
is the same as
def first_x_method(self): ...
first_x_method=logger.log(first_x_method)
and so the logger.log
static method is called with argument func = first_x_method
.
Inside the call to logger.log(first_x_method)
, the sub method __
is defined and returned.
first_x_method=logger.log(first_x_method)
thus sets first_x_method
to refer to the sub method __
.
The parenthesis in first_x_method()
tells Python to call the method first_x_method
.
So x().first_x_method()
first instantiates an instance of the class x, and then calls the method first_x_method
(with x() supplied as the first argument).
Since first_x_method
refers to __
, it is __
that gets called.
A decorator is a function that accepts a function as an argument and returns a "decorated" function that will actually be used when it's called.
In this case logger.log
is passed the function first_x_method
. The actual decorator implementation creates a function that prints a message before and after running its argument, and returns that function (in this case, called ___
). So in effect, every time you call first_x_method
, you're actually calling ____
, which maintains a reference to first_x_method
as func
.
In other words, ____
"captures" first_x_method
and replaces it. Whenever it's called, it will first print something, call the function it has a reference to, and then print something again.
edit: Maybe this simplified example can help you understand it:
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')
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
Output:
Original x <function x at 0x7f3c51e9a758>
Wrapping function <function wrapper at 0x7f3c51e9a7d0>
New x <function wrapper at 0x7f3c51e9a7d0>
Note how x
is now the same as wrapper
. When you call x
, you are actually calling wrapper
.
Do not mark this answer as the answer. Mark one of the others as the answer. The point of this is to correct a misunderstanding so that you may then correctly understand one of the other answers.
精彩评论