Python starter Decorator

Source: Internet
Author: User
Tags wrapper python decorator

Before I speak the Python decorator, I would like to give an example, although a bit dirty, but with the decorator this topic is very appropriate.

Everyone has underwear main function is used to modesty, but in the winter it can not wind and cold for us, how to do? One of the ways we think of it is to change the underwear, let it become thicker and longer, so that it not only has the modesty function, but also provide warmth, but there is a problem, this underwear has been transformed into a pair of trousers, although there is modesty function, but in essence it is no longer a real underwear. So smart people invented trousers, in the premise of not affecting underwear, directly put trousers in the underwear outside, so underwear or underwear, with trousers after the baby no longer cold. Decorator like we say here in the trousers, without affecting the role of underwear under the premise of giving our body to provide a warm effect.

Before you talk about decorators, you have to understand one thing, the functions in Python are different from Java and C + +, and the functions in Python can be passed as arguments to another function like normal variables, for example:

def foo ():     Print ("foo") def Bar (func):    func () bar (foo)

Formally back to our subject. An adorner is essentially a Python function or class that allows other functions or classes to add extra functionality without any code modification, and the return value of the adorner is also a function/class object. It is often used in scenarios where there is a need for facets, such as inserting logs, performance testing, transaction processing, caching, and permission checking, and decorators are a great design for solving such problems. With adorners, we can pull out a lot of similar code that is not related to the function itself to the adorner and continue to reuse it. In summary, the function of an adorner is to add additional functionality to an already existing object.

Let's look at a simple example, although the actual code may be much more complicated than this:

def foo ():     Print ('I am foo')

Now there is a new requirement that you want to record the execution log of the function, and then add the log code in the code:

def foo ():     Print ('I am foo')    logging.info ("foo is running ")

If the function bar () and BAR2 () have similar requirements, how do you do it? Write another logging in the bar function? This creates a lot of identical code, and in order to reduce the duplication of code, we can do this and redefine a new function: To handle the log, and then execute the real business code after the log is processed.

def use_logging (func):    logging.warn ("%s is running" % func. ) __name__)    func()def  foo ():    print('I am Foo') use_logging (foo)

This is logically not a problem, the function is implemented, but we call the time is no longer called the Real business logic foo function, but instead of the use_logging function, which destroys the original code structure, now we have to each time the original Foo function as a parameter to the use _logging function, then there is no better way of it? Of course, the answer is an adorner.

Simple decorator
defuse_logging (func):defwrapper (): Logging.warn ("%s is running"% func.__name__)        returnFunc ()#when Foo is passed in as a parameter, executing func () is equivalent to executing foo ()    returnwrapperdeffoo ():Print('I am foo') Foo= Use_logging (foo)#because the adorner use_logging (foo) returns the Time function object Wrapper, this statement is equivalent to Foo = wrapperFoo ()#executing foo () is equivalent to executing wrapper ()

Use_logging is an adorner, a normal function that wraps the function func that executes the real business logic in it, looks like Foo was decorated with use_logging, and use_logging returns a function called Wrapper In this example, when the function enters and exits, it is called a cross-section, which is called tangent-oriented programming.

@ Grammar sugar

If you've been in touch with Python for a while, you must be a stranger to the @ symbol, right? The @ symbol is the parser's syntax sugar, which is placed where the function begins to define, so you can omit the last step of assigning the value again.

 def   Use_logging (func):  def   wrapper (): Logging.warn (  " %s is running  " % Func.__name__  )  return   func ()  return   wrapper@use_logging  def   foo ():  print  ( " i am foo  "   

As shown above, with @, we can omit foo = use_logging(foo) this sentence, directly call Foo () to get the desired results. Did you see that? the Foo () function does not need to make any changes, just add the adorner to the definition, call it the same as before, and if we have other similar functions, we can continue to invoke the adorner to modify the function without repeating the function or adding a new package. In this way, we improve the reusable nature of the program and increase the readability of the program.

The adorner is so handy in python that the functions that are attributed to Python can be passed as arguments to other functions as normal objects, and can be assigned to other variables, which can be used as return values and can be defined within another function.

*args, **kwargs

Someone might ask, what if my business logic function foo needs parameters? Like what

def foo (name):     Print ("I am%s" % name)

We can specify the parameters when defining the wrapper function:

def wrapper (name):        Logging.warn ("%s is running" % func. ) __name__)        return  func (name)    return Wrapper

The parameters defined by the Foo function can then be defined in the wrapper function. At this point, someone else asked, if the Foo function receives two parameters? What about three parameters? What's more, I might pass a lot of. When the adorner doesn't know how many arguments foo has, we can use *args instead:

def Wrapper (*args):        logging.warn ("%s is running" % func. ) __name__)        return func (*args)    return Wrapper

In this way, I can pass the entire argument to the Func if I don't have to define the number of parameters for Foo. This will not affect the business logic of Foo. Then another reader will ask, if the Foo function also defines some keyword parameters? Like what:

def foo (name, Age=none, height=None):    print("I am%s, age%s, height%s  " % (name, age, height))

At this point, you can specify the keyword function for the wrapper function:

def Wrapper (*args, * *Kwargs)        :#  args is an array, Kwargs a dictionary        Logging.warn ( " %s is running " % func. __name__ return func (*args, * *Kwargs)    return wrapper         
Adorner with parameters

The adorner also has greater flexibility, such as an adorner with parameters, and in the adorner call above, the adorner receives the only parameter that is the function foo that executes the business. The adorner's syntax allows us to provide other parameters, for example, when called @decorator(a) . This provides greater flexibility for the authoring and use of adorners. For example, we can specify the level of the log in the adorner, because different business functions may require varying levels of logging.

defuse_logging (level):defDecorator (func):defWrapper (*args, * *Kwargs):ifLevel = ="warn": Logging.warn ("%s is running"% func.__name__)            elifLevel = ="Info": Logging.info ("%s is running"% func.__name__)            returnFunc (*args)returnwrapperreturndecorator@use_logging ( level="warn")defFoo (name='Foo'):    Print("I am%s"%name) foo ()

The use_logging above is an adorner that allows parameters. It is actually a function encapsulation of the original adorner and returns an adorner. We can interpret it as a closed packet with parameters. When we use @use_logging(level="warn") the call, Python can discover this layer of encapsulation and pass parameters to the adorner's environment.

# @use_logging (level= "warn") is equivalent to @decorator

Class Decorator

Yes, the adorner can be not only a function, but also a class, compared to the function adorner, the class decoration appliance has the advantages of large flexibility, high cohesion, encapsulation, etc. Using the class adorner relies primarily on the method of the class, which is __call__ called when the adorner is attached to the function using the @ form.

classFoo (object):def __init__(Self, func): Self._func=funcdef __call__(self):Print('class Decorator runing') Self._func ()Print('class decorator ending') @FoodefBar ():Print('Bar') bar ()

Functools.wraps

Using adorners has greatly reused the code, but one drawback is that the meta-information of the original function is missing, such as the function docstring , the __name__ parameter list, and the example:

#Decorative Devicedeflogged (func):defWith_logging (*args, * *Kwargs):PrintFunc.__name__      #output ' with_logging '        PrintFunc.__doc__       #Output None        returnFunc (*args, * *Kwargs)returnwith_logging#function@loggeddeff (x):"""does some math"""   returnx + x *xlogged (f)

It is not difficult to find that the function f is with_logging replaced, of course, it becomes the information of the docstring __name__ with_logging function. Fortunately we have functools.wraps , wraps itself is also an adorner, it can copy the original function meta-information into the Func function inside the adorner, which makes the Func function inside the adorner has the same meta-information as the original function foo.

 fromFunctoolsImportWrapsdeflogged (func): @wraps (func)defWith_logging (*args, * *Kwargs):PrintFunc.__name__      #output ' F '        PrintFunc.__doc__       #output ' does some math '        returnFunc (*args, * *Kwargs)returnwith_logging@loggeddeff (x):"""does some math"""   returnx + x * x
Adorner order

A function can also define multiple adorners at the same time, such as:

@a@b@c def f ():     Pass

The order of execution is from inside to outside, the innermost adorner is called first, and finally the outermost adorner is called, which is equivalent to

f = A (b (C (f)))

Reference: https://foofish.net/python-decorator.html

Python starter Decorator

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.