less labor, more.
Decorator has something in common with the meta-programming abstractions introduced before Python: Even without these technologies, you can implement the functionality they provide. As Michele Simionato and I pointed out in the early articles of the lovely Python column, even in Python 1.5, you can implement the creation of Python classes without using a "meta-class" hook.
Decorator's fundamental mediocrity is very similar. The function implemented by Decorator is to modify the functions and methods defined immediately after Decorator. This is always possible, but this functionality is driven primarily by the Classmethod () and Staticmethod () built-in functions introduced in Python 2.2. In the legacy style, you can call Classmethod () as follows:
Listing 1. Typical "old-fashioned" classmethod
Class C: def foo (cls, y): print "Classmethod", cls, y foo = classmethod (foo)
Although Classmethod () is a built-in function, it is not unique; You can also use your own method to convert functions. For example:
Listing 2. Transformation of a typical "legacy" method
def enhanced (Meth): def new (self, y): print "I am Enhanced" return meth (self, y) return newclass C: def bar (self, x): print "Some method says:", x bar = Enhanced (bar)
All decorator does is to prevent you from reusing the method name and placing decorator in the first place in the method definition, mentioning its name. For example:
Listing 3. Typical "old-fashioned" classmethod
Class C: @classmethod def foo (cls, y): print "Classmethod", cls, y @enhanced def bar (self, x): print "Some method says:", X
Decorator can also be used with regular functions in the same way as methods in a class. Surprisingly, all this is so simple (strictly speaking, even unnecessary) that simply modifying the syntax requires a simple modification of the grammar, everything works better and makes the process easier to justify. You can link decorator together by listing multiple decorator before the function defined by the method, and good judgment helps prevent too many decorator linking together, but sometimes it makes sense to link a few decorator together:
Listing 4. Link Decorator
@synchronized @loggingdef myfunc (arg1, Arg2, ...): # ... do something# decorators is equivalent to ending with:# m Yfunc = synchronized (logging (myfunc)) # Nested in that declaration order
Decorator is just a syntactic sugar, and if you are too eager, it will make you lift the stone and hit your foot. Decorator is actually a function with at least one parameter--the programmer is responsible for ensuring that the return content of decorator is still a meaningful function or method, and implements the original function to make the connection useful. For example, the following is an incorrect usage of decorator two:
Listing 5. There is no error returning the function decorator
>>> def spamdef (FN): ... print "spam, spam, spam" ...>>> @spamdef ... def useful (A, B): ... Print a**2 + b**2...spam, spam, spam>>> useful (3, 4) Traceback (most recent call last): File "
", Lin E 1, in? TypeError: ' Nonetype ' object is not callable
Decorator may return a function, but there is no meaningful association between this function and the unmodified function:
Listing 6. Ignoring the decorator of incoming functions
>>> def spamrun (FN): ... def sayspam (*args): ... print "spam, spam, spam" ... Return sayspam...>>> @spamrun ... def useful (A, B): ... Print a**2 + b**2...>>> useful (3,4) spam, spam, spam
Finally, a better performing decorator can enhance or modify the operation of an unmodified function in some ways:
Listing 7. Decorator modifying the behavior of an unmodified function
>>> def addspam (FN): ... def new (*args): ... print "spam, spam, spam" ... RETURN fn (*args) ... Return new...>>> @addspam ... def useful (A, B): ... Print a**2 + b**2...>>> useful (3,4) spam, spam, spam25
You may wonder how useful useful () is. Addspam () is it really that great enhancement? But this mechanism is at least as good as the pattern you can usually see in useful decorator.
Introduction to Advanced Abstraction
In my experience, the most common scenario for a meta-class is to modify the methods in the class after the class instantiation. Decorator does not currently allow you to modify the class instantiation itself, but they can modify the methods that are attached to the class. This does not allow you to dynamically add or remove methods or class properties during instantiation, but it allows these methods to change their behavior at run time based on the conditions of the environment. Now technically, decorator is applied when you run the class statement, which is closer to "compile time" than "runtime" for the top-level class. But scheduling decorator's run-time decisions is as simple as creating a class factory. For example:
Listing 8. Robust but deeply nested decorator
def arg_sayer: def what_sayer (meth): def new (self, *args, **KWS): print "What return" meth (self, * args, **KWS) return new return What_sayer