Learn more about using the decorator in Python

Source: Internet
Author: User
Tags python decorator
@ This operator makes the decorator very eye-catching in Python code. the use of the decorator also contains many advanced skills in Python programming. here we will learn how to use the decorator in Python. Decorator vs decorator mode
First of all, you need to understand that the word "decorator" may have a lot to worry about, because it is easy to be confused with the decorator mode in the design model book. I once considered to give this new feature some other terms, but the decorator eventually won.
Indeed, you can use python decorator to implement the decorator mode, but this is definitely a small part of its functionality, a bit violent. For python decorators, I think they are the closest to macros.

Macro History
Macros have a long history, but most people may have the experience of using C language to pre-process macros. However, for macros in C language, there are some problems. (1) macros do not exist in C language, (2) and macro behavior is sometimes a bit strange, and it is often different from the C language.
To support operations on some elements of the language itself, annotations are added to both Java and C. Of course, they also have some problems: sometimes you have to bypass many pitfalls to achieve your goals. These annotation features are also tied to some inherent features of these languages (like the "Directing" described by Martin Fowler ")
A little different is that many C ++ programmers, including me, have realized the power of C ++ templates and are using this function like using macros.
Many other languages also contain macro functions. although I do not know much about it, I am willing to say it without saying anything, the python decorator is similar to the Lisp macro in terms of both powerful and rich functions.

Macro goals
In my opinion, macro is not described too much: macros exist in a programming language to provide the ability of the operating language element itself. This is exactly what the python decorators can do. they can modify functions and decorate the class. Compared to complex meta-classes, this may be the reason why we often provide a simple decoration device.
Most programming languages provide a major disadvantage in self-modification (meta-programming) solutions, that is, there are too many restrictions and constraints, and there is an illusion of writing in other languages.
Python complies with Martin Fowler's "Enabling" programming language. So what if you want to make changes (meta-programming) in a different or restricted language for Mao? Why don't I just start copying python and start it? This is what the python decorator can do.

What can I do with the Python decorator?
The decorator allows you to "inject" or "modify" the code (logic) in a function or class ). In addition to being simpler and more powerful, the decorator sounds a bit like the aspect-oriented programming of AOP, right. For example, you want to do something before or after a method (for example, some common operations in aspect-oriented programming such as permission check, tracking, resource locking ). With the decorator, you can do this:

@entryExitdef func1():  print "inside func1()"@entryExitdef func2():  print "inside func2()"

Function decorators
The function decorators are usually placed in a function-defined code to apply qualified decorators. for example:

@myDecoratordef aFunction():  print "inside aFunction"

When the compiler goes through this code, the aFunction function will be compiled, and the compiled function object will be passed to myDecorator. the modifier will generate a new function object to replace the original function aFunction.
So what is the code implementation of the decorator myDecorator? Although most of the decorator entry examples write a function, I found that the class decorator can be better understood and more powerful than the functional decorator.
The only thing that needs to be ensured is that the objects returned by the decorator can be called like functions. Therefore, the class decorator must implement _ call __.
What should I do with the decorator? Well, it can do anything, but in general, you may expect the original passed function to be executed somewhere, although this is not mandatory:

class myDecorator(object):  def __init__(self, f):    print "inside myDecorator.__init__()"    f() # Prove that function definition has completed  def __call__(self):    print "inside myDecorator.__call__()"@myDecoratordef aFunction():  print "inside aFunction()"print "Finished decorating aFunction()"aFunction()

When you execute this code, you will see the following output:

inside myDecorator.__init__()inside aFunction()Finished decorating aFunction()inside myDecorator.__call__()

Note that the constructor of myDecorator is actually executed when decorating the function. We can call function f in _ init _ (). we can see that function f () has been called before the modifier is called. In addition, the constructor of the decorator can receive the method of decoration. In general, we will capture this function object and then call it in function _ call. Decoration and calling are two very clear and different steps, which is why I say similar decorator is simpler and more powerful.
When the aFunction function is decorated and then called, we get a completely different behavior, actually executing myDecorator. the code logic of _ call _ () is because "decoration" replaces the original code logic with the new returned logic. In our example, the myDecorator object replaces the aFunction function. In fact, before the modifier operator @ is added, you have to do some relatively low operations to accomplish the same thing:

def foo(): passfoo = staticmethod(foo)

With the @ operator, you can get the same result elegantly:

@staticmethoddef foo(): pass

However, many people oppose the decorator because of this, but @ is only a small syntactic sugar. it passes a function object to another function and then replaces the original method with the return value.
I think the reason why the decorator has such a big impact is that this small syntactic sugar has completely changed the way people think about programming. Indeed, by implementing it into a programming language structure, it brings the idea of "applying code to code" to the mainstream programming thinking layer.

Blue
Now let's implement the first example. Here we will do some common things and use the code:

class entryExit(object):  def __init__(self, f):    self.f = f  def __call__(self):    print "Entering", self.f.__name__    self.f()    print "Exited", self.f.__name__@entryExitdef func1():  print "inside func1()"@entryExitdef func2():  print "inside func2()"func1()func2()

The running result is:

Entering func1inside func1()Exited func1Entering func2inside func2()Exited func2

Now we can see that the decorated methods have tracking information of "entering" and "leaving.
The Constructor stores the function objects passed in through Parameters. in the called method, we use the _ name _ attribute of the function object to display the name of the called function, then call the decorated function itself.

Use a function as the decorator
There is only one constraint on the result returned by the decorator, that is, it can be called, so that it can reasonably replace the previously decorated function. In the above examples, we replace the original function with an object containing _ call. A function object can also be called, so we can use the function to override the example of the previous decorator, like this:

def entryExit(f):  def new_f():    print "Entering", f.__name__    f()    print "Exited", f.__name__  return new_f@entryExitdef func1():  print "inside func1()"@entryExitdef func2():  print "inside func2()"func1()func2()print func1.__name__


The new_f () function is nested in the method body of entryExit. when entryExit is called, new_f () is also returned in turn. It is worth noting that new_f () is a closure that captures the value of parameter variable f.
After new_f () is defined, it will be returned by entryExit, and the modifier mechanism will assign the result to the new method to be decorated.
The output of code print func1. _ name _ is new_f, because the original method has been replaced with new_f in the decoration process. if this is a problem for you, you can modify the function name before the decorator returns the result:

def entryExit(f):  def new_f():    print "Entering", f.__name__    f()    print "Exited", f.__name__  new_f.__name__ = f.__name__  return new_f

You can dynamically obtain the function information including the changes you made, which is very useful in python.

Decorator with parameters
Now let's make a simple change to the example above to see what will happen when the modifier parameter is added:

class decoratorWithArguments(object):  def __init__(self, arg1, arg2, arg3):    """    If there are decorator arguments, the function    to be decorated is not passed to the constructor!    """    print "Inside __init__()"    self.arg1 = arg1    self.arg2 = arg2    self.arg3 = arg3  def __call__(self, f):    """    If there are decorator arguments, __call__() is only called    once, as part of the decoration process! You can only give    it a single argument, which is the function object.    """    print "Inside __call__()"    def wrapped_f(*args):      print "Inside wrapped_f()"      print "Decorator arguments:", self.arg1, self.arg2, self.arg3      f(*args)      print "After f(*args)"    return wrapped_f@decoratorWithArguments("hello", "world", 42)def sayHello(a1, a2, a3, a4):  print 'sayHello arguments:', a1, a2, a3, a4print "After decoration"print "Preparing to call sayHello()"sayHello("say", "hello", "argument", "list")print "after first sayHello() call"sayHello("a", "different", "set of", "arguments")print "after second sayHello() call"

From the output results, the running effect has changed significantly:

Inside __init__()Inside __call__()After decorationPreparing to call sayHello()Inside wrapped_f()Decorator arguments: hello world 42sayHello arguments: say hello argument listAfter f(*args)after first sayHello() callInside wrapped_f()Decorator arguments: hello world 42sayHello arguments: a different set of argumentsAfter f(*args)after second sayHello() call

Currently, in the "decoration" phase, constructors and _ call _ () are called sequentially, and __call _ () only accept parameters of the function object type, in addition, you must return a decoration method to replace the original method, __call _ () will only be called once in the "decoration" phase, then, the returned decoration method is actually used in the call process.
Although this behavior is reasonable, the constructor is now used to capture parameters of the decorator, and _ call _ () cannot be used as a decoration method, instead, use it to complete the decoration process. Even so, the first time I saw this very different behavior from the decorator without parameters, it would be surprising, and their programming paradigm is quite different.

Function decorators with parameters
Finally, let's take a look at the more complex functional decorators. here you have to do everything at once:

def decoratorFunctionWithArguments(arg1, arg2, arg3):  def wrap(f):    print "Inside wrap()"    def wrapped_f(*args):      print "Inside wrapped_f()"      print "Decorator arguments:", arg1, arg2, arg3      f(*args)      print "After f(*args)"    return wrapped_f  return wrap@decoratorFunctionWithArguments("hello", "world", 42)def sayHello(a1, a2, a3, a4):  print 'sayHello arguments:', a1, a2, a3, a4print "After decoration"print "Preparing to call sayHello()"sayHello("say", "hello", "argument", "list")print "after first sayHello() call"sayHello("a", "different", "set of", "arguments")print "after second sayHello() call"

Output result:

Inside wrap()After decorationPreparing to call sayHello()Inside wrapped_f()Decorator arguments: hello world 42sayHello arguments: say hello argument listAfter f(*args)after first sayHello() callInside wrapped_f()Decorator arguments: hello world 42sayHello arguments: a different set of argumentsAfter f(*args)after second sayHello() call

The return value of the function modifier must be a function that can wrap the original function. That is to say, Python will get and call the returned function result when the decoration occurs, and then pass it to the decorated function, this is why Layer-3 functions are nested in the implementation of the decorator. the function at the bottom layer is a new replacement function.
Because of the characteristics of the closure, wrapped_f () can access these parameters without displaying the values stored in arg1, arg2, and arg3 as in the case of a class decoration. However, this happens to be an example of "explicit is better than implicit. Although functional decorators may be simpler, they are easier to understand and therefore easier to modify and maintain.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.