问题
Consider the following code:
class C:
def decorator(self):
print(self)
def inner(*args):
return self(*args)
return inner
@decorator
def decorated(self):
...
if __name__ == '__main__':
c = C()
c.decorator() # 1
c.decorated() #2
The output is this:
<function C.decorated at 0x1121cd9d8>
<__main__.C object at 0x111f5a8d0>
- When the
decorator
function is called as a normal class function, the first argument it receives is a class instance. - However, when
decorated
is called and thedecorator
function is used as a decorator, the first argument is adecorated
function; thedecorator
function now behaves virtually the same as a normal function that’s defined outside of the class.
Intuitively, since the decorator
function is defined as a function inside a class without the @staticmethod
or @classmethod
decorator, I thought the first argument it receives should always be the instance of the class. However this was not the case.
I don’t see this coding pattern often, but is this coding pattern widely used? What are the possible pitfalls of using this coding pattern?
回答1:
I made a little change to your code and I tried to explain what happens in the comments:
class C:
def decorator(self, count=[0]):
print(count, ":", self)
count[0] += 1
def inner(*args):
return self(*args)
return inner
@decorator
def decorated(self):
print(self)
if __name__ == '__main__':
c = C() #0 Applies the decorator like
# decorated = decorator(decorated)
# Above, self equals decorated function, and gets printed
# inside decorator function.
# The decorated function is now a function that returns decorated(*args), actually None, but prints self, at call moment (not now).
c.decorator() #1 normal bound method call, self is c
print("[2] : ", end="") # for visual reference
c.decorated() #2 calls C.decorated(c) so now self is c again and decorated is the result of decorated(c) that returns None but prints self (equals c), with no print from the decorator bound method, that does not run.
Outputs:
[0] : <function C.decorated at 0x7f26945b4ee0>
[1] : <__main__.C object at 0x7f26945bddc0>
[2] : <__main__.C object at 0x7f26945bddc0>
I think this use is a bit confusing, so unless you have a concrete reason to do that... However I have seen some advanced code with decorators or metaclasses that can be difficult to grasp. It is always a matter of really needing something worth of it. I suggest you search for typical decorator and class decorator uses. Rarely seen looks different from widely used.
回答2:
It seem the "problem" its actually at init
class C:
def decorator(self):
print(self)
def inner(*args):
return self(*args)
return inner
@decorator
def decorated(self):
pass
if __name__ == '__main__':
c = C()
#c.decorator() # 1
#c.decorated() #2
python test.py
<function C.decorated at 0x00000289DD5BE160>
So... at init of a class with decorated functions first it modifies the function (to launch the decorator instead of the decorated). Probably there is some documentation that can be read. I never used decorators inside the class, only stand alone.
来源:https://stackoverflow.com/questions/63967686/why-does-python-class-method-s-first-argument-change-when-used-as-decorator