Systematic learning of Python - Decorators: Function decorators - [Decorating methods: basic knowledge]

Category Catalog: General Catalog of "Systematic Learning Python"


When we wrote the first class-based tracerfunction decorator in the previous article, we simply assumed that it should also apply to any method - the decorated method should work the same, and the instance selfparameters should be Contained directly in *argsfront of. But the only practical drawback to this assumption is that it is flat-out wrong! When applied to a class method, tracerthe first version of does not work because selfit is an instance of the decorator class, and the instance of the main class being decorated is not included *args. This is true in both python3.X and Python2.X.

Now we can see this in the context of actual working code. Assume the class-based tracking decorator is as follows:

class tracer:
	def __init__(self, func):
		self.calls = 0
		self.func = func
	
	def __call__(self, *args, **kwargs):
		self.calls += 1
		print('call %s to %s' % (self.calls, self.func.__name__))
		return self.func(*args, **kwargs)

@tracer
def spam(a, b, c):
	print(a + b + c)

We can get the following output:
Output results
However, the decoration of the class level method fails:

class Person:
	def __init__(self, name, pay):
		self.name = name
		self.pay = pay

	@tracer
	def giveRaise(self, percent):
		self.pay *= (1.0 + percent)

	@tracer
	def lastName(self):
		return self.name.split()[-1]

We can get the following output:
Output results
The root of the problem here is that the parameter of tracerthe class __call__method selfis an tracerinstance, or an Personinstance? In fact, we need both: tracerto record the decorator state and Personto point to the original method. In fact, selfit must be traceran object that provides access to tracerstate information (its callssum func); this is true whether decorating a simple function or decorating a method.

Unfortunately, when we __call__rebind the decorated method name to a class instance object, Python only selfpasses tracerthe instance; it doesn't pass the body in the argument list at all Person. Furthermore, since tracer 不知道我们要利用方法调用处理的has no information about the Person` instance, there is no way to create a bound method with the instance and dispatch the call correctly. This isn't a bug, but it's a very noteworthy detail.

Finally, the previous list ended up passing too few parameters to the decorated method and resulted in an error. Verify this by adding a line to the decorator __call__method that prints all arguments - as we can see, selfthere is an tracerinstance, while Personthe instance is completely missing:
Output results
as mentioned earlier, this happens because only Python only passes the implicit body instance when a method name is bound to a simple function self; when it is an instance of a callable class, it passes selfan instance of that class. Technically, Python creates a bound method object that contains the principal instance only if the method is a simple function, rather than a callable instance of another class.

References:
[1] Mark Lutz. Python Learning Manual[M]. Machinery Industry Press, 2018.

Guess you like

Origin blog.csdn.net/hy592070616/article/details/135330368