Consider the following to decorators
class MethodDecoratorC(object):
def __init__(self,func):
self.func = func
def __call__(self,*args,**kwargs):
print(len(args))
print(len(kwargs))
self.func(*args,**kwargs)
def method_decorator_f(func):
def wrapped_func(*args,**kwargs):
print(len(args))
print(len(kw开发者_JS百科args))
func(*args,**kwargs)
return wrapped_func
They look to do exactly the same, and for functions that's true:
@MethodDecoratorC
def test_method_c(a):
print(a)
@method_decorator_f
def test_method_f(a):
print(a)
test_method_f("Hello World! f")
test_method_c("Hello World! c")
prints:
1
0
Hello World! f
1
0
Hello World! c
For methods however, something very strange happens:
class TestClass(object):
@MethodDecoratorC
def test_method_c(self,a):
print(a)
@method_decorator_f
def test_method_f(self,a):
print(a)
t = TestClass()
t.test_method_f("Hello World! f")
t.test_method_c("Hello World! c")
prints:
2
0
Hello World! f
1
0
Traceback (most recent call last):
File "test5.py", line 40, in <module>
t.test_method_c("Hello World! c")
File "test5.py", line 8, in __call__
self.func(*args,**kwargs)
TypeError: test_method_c() takes exactly 2 arguments (1 given)
Not much expected! Somehow the TestClass object is not transferred as an argument to the __call__
method of my decorator object.
Why this difference? And is there a way that I can still obtain the object in my class style decorator?
self
being bound to the first argument for instance methods works only because methods are wrapped in descriptors. When obj.meth
is requested, not found in the object and then found in the class, the descriptor's __get__
method is called with some information including the object, and returns a wrapper around the actual method object that, when called, calls the underlying method with the object as additonal/first argument (self
).
These descriptors are only added for actual functions, not for other callable objects. To make a class with a __call__
method work like a method, you'll have to implement a __get__
method (see link above).
精彩评论