Python decorator detailed

Source: Internet
Author: User
Tags python decorator
The adorner in Python is a very useful thing, and we can write some specific methods and common methods into a decorator, which provides a great convenience for invoking these methods, thus improving the readability and simplicity of our code, as well as extensibility.

Before learning the Python decorator, let's take a look at one of these examples:

First, scope

# coding:utf-8 msg = ' Hello test1 '  def Add ():    msg = ' This is add ' Print    msg     #当执行add () will print ' This is add ' def ADD2 ():    print msg     #当执行add2 () when ' Hello Test1 ' will be printed

The above example simply explains the scope of Python, which states that the global variable Msg,add function also declares a local variable msg, which, when executing the "Print msg" of Add (), will first find if there is a local variable msg in the Add. If you don't find it, go to the top-level scope to find out if the variable exists, local variables are generated when the function is run, and local variables are destroyed when the function ends, and then we deepen the example above:

Second, closed package

# Coding:utf-8 def add ():    msg = ' Hello add '    def inner (): Print        msg               #运行add () here will be printed ' Hello Add '    return Inner

>>> obj = Add () >>> obj            #可见obj是一个指向inner函数的对象 <function inner at 0x0000000002855438>...> >> obj () hello add           #运行obj () print msg

See the above example is not a little puzzled, obj is the object that points to the inner function, when running obj is equivalent to running inner, but the add function is not running, that is, MSG is not declared at the same time inner also does not declare the variable MSG, How does it find the value of the MSG variable?

This is the "closure" in Python, where Python supports a feature called a function closure, which, in the words of the word, is that a function nested within a non-global scope remembers the enclosing namespace it was in when it was defined. This can be concluded by looking at the Obj.func_closure property of the function, which contains the value within the enclosing scope (only the value that is captured, and if other values are defined in the Add, the enclosing scope is not) the closure is the core principle of the Python decorator. Next we write a simple decorator example:

Three, simple decorative device

# Coding:utf-8 def check_args_num (func):    # The adorner is used to check the number of parameters passed in, check if it is two parameters    def inner (*args, **kwargs):        args_ List = list (args)        if Len (args_list) < 2:             for I in range (2-len (args)):                # Fill in 0 if the number of parameters is less than 2                Args_list.app End (0)        if Len (args_list) > 2:            # Prints error if number of arguments is greater than 2 print ' The args number is            too many! '        Func (*args_list, **kwargs)    return inner  @check_args_numdef Add (x, y):    return x + y

Execution Result:

>>>print Add (3...>>>print) 100...>>>print Add (+) Traceback (most recent Call last):  File "D:\PyCharm 5.0.4\helpers\pydev\pydevd_exec.py", line 3, exec    exec exp in global_vars, Local_ VARs  file ' <input> ', line 1, in <module>  file ' e:/code/my_project/decorator/test1.py ', line +, in Inner    Raise Exception (' The args number is too many! ') Exception:the args number is too many!... >>>add<function inner at 0x0000000002a6c3c8># You can see that the Add function now points to inner

Four or more adorners

# Coding:utf-8 def check_args_int (func):    # This adorner is used to check if an incoming parameter is an int type    def ensure_int (*args, **kwargs): from        Array Import Array        try:            Array (' H ', args)        except Exception, E:            raise Exception (E)        return func (* args, **kwargs)     return ensure_int  def check_args_num (func):    # The adorner is used to check the number of parameters passed in and check if it is a two-parameter    def inner (*args, **kwargs):        args_list = list (args)        if Len (args_list) < 2: for             I in range (2-len (args)):                # as If the number of parameters is less than 2, use 0 to fill                args_list.append (0)        if Len (args_list) > 2:            # Print Error if the number of arguments is greater than 2            raise Exception (' The args number is too many! ')        return func (*args_list, **kwargs)     return inner  @check_args_num @check_args_intdef Add (x, y):    return x + Y

This adds a parameter content check adorner, when more than one adorner, will be executed from top to bottom, first execute check_args_num and then execute Check_args_int, execute the result:

>>> Print Add (1, ' Fsaf ') Traceback (most recent call last):  File "D:\PyCharm 5.0.4\helpers\pydev\pydevd_ exec.py ", line 3, exec    exec exp in global_vars, Local_vars  File" <input> ", line 1, in <module>  File "e:/code/my_project/decorator/test1.py", line inner    return func (*args_list, **kwargs)  file "e:/ code/my_project/decorator/test1.py ", line ten, in Ensure_int    raise Exception (e) exception:an integers is required...& Gt;>> add<function Inner at 0x0000000002b1c4a8>

#这里可以看到add还是指向inner, we can understand that when multiple adorners are present, when add is called, its invocation entry is always the first adorner, the first adorner finishes executing, then the next, and the parameters are passed down sequentially

Five, with parameters of the adorner

We know that when we define the adorner, the first parameter to the adorner is the decorated function (eg. add in the example), and sometimes we need to pass additional parameters to the adorner, the following example will pass additional parameters to the adorner, as follows:

# Coding:utf-8 def check_args_int (func): # This adorner is used to check if an incoming parameter is an int type def ensure_int (*args, **kwargs): from array I Mport Array Try:array (' H ', args) except Exception, E:raise Exception (e) retur n Func (*args, **kwargs) return Ensure_int def check_args_num (flag): "':p aram Func: Decorated function:p Aram flag: Decide whether Check the number of parameters ' ' # The adorner is used to check the number of parameters passed in, check if it is two parameters Def get_func (func): Def inner (*args, **kwargs): if FLA                G = = ' false ': print ' Skip check! ' return func (*args, **kwargs) args_list = List (args) If Len (args_list) < 2:for i In range (2-len (args)): # 0 to fill args_list.append (0) If Len (args) If the number of parameters is less than 2            _list) > 2: # Print error if the number of parameters is greater than 2 raise Exception (' The args ' is too many! ') return func (*args_list, **kwargs) return inner return Get_func @check_args_num (' false ') @check_args_intdef Add (x, y): return x + y 

This example only check_args_num and before the difference is that the adorner check_args_num more than one parameter flag, when the flag== ' false ', skip the parameter number check, the following is the output

>>>print Add (1, 2) Skip check!3

Six, decorator without parameters of the adorner

The Decorator module is a module used by Python to specialize in encapsulating adorners, and it is easier to construct adorners using decorator, while the decorated function signatures remain unchanged.
The previous talk about the implementation of the Python decorator construction via closures, where the Python adorner is implemented using the Decorator module, the same principle.

From decorator import decorator  @decoratordef Check_num (func, *args, **kwargs):    If Len (args)! = 2:        raise Exception (' Your args number is not ')    else:        return func (*args, **kwargs)  @check_numdef Add (x, y):    return x + y

>>> add<function Add at 0x0000000002d43ba8>>>> Print Add (3...>>> add) Traceback (most recent): File "D:\PyCharm 5.0.4\helpers\pydev\pydevd_exec.py", line 3, EXEC exec exp in g Lobal_vars, Local_vars File "<input>", line 1, in <module>typeerror:add () takes exactly 2 arguments (3 given When #可以看到这里当我们传三个参数给add () function, he throws a type error exception directly from the Add () function, #并没有进入check_num装饰器进行参数校验, the signature of the decorated function add () or his own # if the adorner is directly constructed, Then this will throw an exception from Check_num, as follows: Def check_num (func): Def inner (*args,**kwargs): If Len (args)! = 2:raise E Xception (' Your args number is not ') Else:return func (*args,**kwargs) return inner @check_numdef Add (x, y): return x + y >>> add<function inner at 0x0000000002e233c8>>>>add () Traceback (M OST recent call last): File "D:\PyCharm 5.0.4\helpers\pydev\pydevd.py", line 2411, in <module> globals = Debugge R.run (setup[' file '], none, none, Is_module) file "D: \pycharm 5.0.4\helpers\pydev\pydevd.py ", line 1802, in Run launch (file, globals, locals) # Execute the script file" e:/code/my_project/decorator/test3.py ", line <module> Print Add (+/-) File" E:/code/my_project/decorator /test3.py ", line 4, in inner raise Exception (' Your args number was not ') ' Exception:your args number was not '

As can be seen from the above example, when the adorner is constructed by means of a closure, its execution function entry is a nested function in the adorner, so that the problem may arise above, and the inner function will be executed when add (t/a) executes (if there is no parameter check inside the inner, it will not throw an exception. , only the Add (x, y) function is actually called when executing to return func (*args,**kwargs), which throws an exception, which causes the program to execute extra code, wasting memory, CPU.

Seven, decorator with parameters of the adorner

What if you want the adorner to take parameters?

From decorator import decorator  def check (flag):    @decorator    def check_num (func, *args, **kwargs):        if Flag = = ' false ':            print ' skip check! '            return func (*args,**kwargs)        if Len (args)! = 2:            raise Exception (' Your args number is not ')        else:            return func (*args, **kwargs)    return check_num  @check (' false ') def add (x, y):    return x + y >>>add <function Add at 0x0000000002c53c18>>>>add skip Check!3

Decorator module Here, this module is relatively simple, there are some features do not talk about the view of the source code is clear, the principle is the use of Python closures implemented

Viii. Functools.wraps (func) Adorner

The functions of the functools.wraps and decorator modules are the same, all to solve the signature problem of the decorated function, here we only enumerate a parameter example with this kind of decorator construction method:

Import Functools def check (flag):    def Wraps (func):        @functools. Wraps (func)        def check_num (*args, **kwargs) :            If flag = = ' false ':                print ' skip check! '                return func (*args,**kwargs)            if Len (args)! = 2:                raise Exception (' Your args number is not ')            else:                return func (*args, **kwargs)        return check_num    return wraps @check (' false ') def add (x, y):    return x + y

Compared with the example above through the Decorator module decoration function, we can find that the code with decorator decoration function is more concise and understandable, but they are more efficient to execute? Below we test by the timer:

From Timeit Import timer print timer (' Add ', setup= ' from __main__ import add '). Timeit (100000) #将该段代码 added in the previous example Line 100,000 times

Functools.wraps Decoration Execution Result:

2.37299322602

Decorator module Decoration Execution Result:

3.42141566059

Execution efficiency wraps slightly higher, but here is performed 100,000 times the gap between them is about 1 seconds, so I personally still prefer to use Decorator module decoration function, after all, it seems easy to understand, the wording is easier! This article will introduce the adorner here, of course, does not say that the use of decorative decorations, such as: Decorative class ... Its principle is to use the class as a decorator, the class inside need to use the __call__ method, as for the use of decorative class interested friends Baidu!

  • Related Article

    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.