Read this article to understand the Python decorator.

Source: Internet
Author: User
Tags python decorator
Before talking about the Python decorator, I would like to give an example. although it is a bit dirty, it is very relevant to the topic of the decorator. Before talking about the Python decorator, I would like to give an example. although it is a bit dirty, it is very relevant to the topic of the decorator.

The main function of the underwear that everyone has is to block shame. but in the winter, it cannot protect us against the cold. what should I do? One way we can think of is to transform the underwear to make it thicker and longer. in this way, it not only has the shame function, but also provides warmth, but there is a problem, after being transformed into a pair of trousers, this underwear is essentially no longer a real underwear, even though it has a shame function. So the smart people invented trousers, without affecting the underpants, directly put the trousers outside the underpants, so that the underpants or underpants, with the trousers after the baby is no longer cold. The decorator is like the trousers we mentioned here. without affecting the effect of the underwear, it provides us with a warm effect.

Before talking about the decorator, you must first understand one thing. the functions in Python are not the same as those in Java and C ++, functions in Python can be passed to another function as parameters like common variables. for example:

def foo():    print("foo")def bar(func):    func()bar(foo)

Officially return to our topic. The decorator is essentially a Python function or class that allows other functions or classes to add additional functions without any code modifications, the return value of the decorator is also a function/class object. It is often used in scenarios with specific requirements, such as log insertion, performance testing, transaction processing, caching, and permission verification. the decorator is an excellent design to solve such problems. With the decorator, we can extract a large number of similar code irrelevant to the function itself into the decorator and continue to reuse it. In summary, the purpose of the decorator is to add additional functions to existing objects.

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 to record the execution log of the function, so add the log code to the code:

def foo():    print('i am foo')    logging.info("foo is running")

If bar () and bar2 () functions have similar requirements, how can this problem be solved? Write another logging in the bar function? In this way, a large number of identical codes are generated. to reduce repeated code writing, we can redefine a new function to process logs and then execute the real business code after logs are processed.

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

In this way, there is no logic problem, and the function is implemented. However, we do not call the real business logic foo function, but instead use the use_logging function, this breaks the original code structure. now we have to pass the original foo function as a parameter to the use_logging function every time. Is there a better way? Of course, the answer is the decorator.

Simple decorator

Def use_logging (func): def wrapper (): logging. warn ("% s is running" % func. _ name _) return func () # When passing foo as a parameter, executing func () is equivalent to executing foo () return wrapperdef foo (): print ('I am Foo') foo = use_logging (foo) # The function object wrapper returned by the decorator use_logging (foo). This statement is equivalent to foo = wrapperfoo () # executing foo () is equivalent to executing wrapper ()

Use_logging is a decorator. it is a common function that wraps func, which executes the real business logic. it looks like foo is decorated by use_logging, and use_logging returns a function, the name of this function is wrapper. In this example, a function is called a cross-plane when it enters and exits. this programming method is called a cross-plane programming method.

@ Syntactic sugar

If you have been familiar with Python for a while, you must be familiar with the @ symbol. Yes, the @ symbol is the syntactic sugar of the decorator. it is placed in the place where the function is defined, in this way, the next assignment operation can be omitted.

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

As shown above, with @, we can save the sentence foo = use_logging (foo) and directly call foo () to get the desired result. No, the foo () function does not need to be modified. you only need to add the decorator in the defined part. it is still called as before. if we have other similar functions, we can continue to call the modifier to modify the function, instead of modifying the function repeatedly or adding a new encapsulation. In this way, we have improved the reusability of the program and increased the readability of the program.

The convenience of using the decorator in Python is attributed to the fact that a Python function can be passed as a parameter to other functions like a common object and can be assigned to other variables and can be used as a return value, can be defined in another function.

* Args, ** kwargs

Someone may ask, what if my business logic function foo needs parameters? For example:

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

In this way, the parameters defined by the foo function can be defined in the wrapper function. Now, someone has to ask, what if the foo function receives two parameters? What about the three parameters? What's more, I may Upload many. When the decorator does not know how many parameters 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, the number of parameters defined by render manager foo can be completely passed to func. This will not affect foo's business logic. Some readers may ask, what if the foo function defines some keyword parameters? For example:

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

In this case, you can specify the keyword function for the wrapper function:

Def wrapper (* args, ** kwargs): # args is an array, and kwargs is a dictionary logging. warn ("% s is running" % func. _ name _) return func (* args, ** kwargs) return wrapper

Decorator with parameters

The decorator has more flexibility. for example, a decorator with parameters. in the above decorator call, the decorator receives a unique parameter that is the function foo that executes the business. The decorator syntax allows us to provide other parameters when calling, such as @ decorator (). In this way, it provides more flexibility for writing and using the decorator. For example, you can specify the log level in the decorator, because different service functions may require different log levels.

def use_logging(level):    def decorator(func):        def wrapper(*args, **kwargs):            if level == "warn":                logging.warn("%s is running" % func.__name__)            elif level == "info":                logging.info("%s is running" % func.__name__)            return func(*args)        return wrapper    return decorator@use_logging(level="warn")def foo(name='foo'):    print("i am %s" % name)foo()

The preceding use_logging is a decorator with parameters. It is actually a function encapsulation of the original decorator and returns a decorator. We can understand it as a closure with parameters. When we call it using @ use_logging (level = "warn"), Python can discover the encapsulation of this layer and pass the parameters to the decorator environment.

@ Use_logging (level = "warn") is equivalent to @ decorator

Class decorator

Yes, the decorator can be a function or a class. compared with the function decorator, the decorator has the advantages of flexibility, high cohesion, and encapsulation. The class decorator mainly relies on the _ call _ method of the class. this method is called when the decorator is appended to a function in the @ form.

class Foo(object):    def __init__(self, func):        self._func = func    def __call__(self):        print ('class decorator runing')        self._func()        print ('class decorator ending')@Foodef bar():    print ('bar')bar()

Functools. wraps

The code is greatly reused with the decorator, but one drawback is that the metadata of the original function is missing, such as docstring, _ name _, and parameter list of the function. let's look at the example first:

# Decorator def logged (func): def with_logging (* args, ** kwargs): print func. _ name _ # output 'with _ logging' print func. _ doc _ # output None return func (* args, ** kwargs) return with_logging # function @ loggeddef f (x ): "does some math" return x + x * xlogged (f)

It is not difficult to find that function f is replaced by with_logging. of course, its docstring ,__ name _ is the information of the with_logging function. Fortunately, we have functools. wraps and wraps are also decorator, which can copy the metadata of the original function to the func function in the decorator, this makes the func function in the decorator have the same metadata as the original function foo.

From functools import wrapsdef logged (func): @ wraps (func) def with_logging (* args, ** kwargs): print func. _ name _ # output 'F' print func. _ doc _ # output 'does some math' return func (* args, ** kwargs) return with_logging @ loggeddef f (x ): "does some math" return x + x * x

Decorative device sequence

A function can also define multiple decorators at the same time, for example:

@a@b@cdef f ():    pass

The execution sequence is from the inside to the outside. it first calls the decorator of the innermost layer, and finally calls the decorator of the outermost layer, which is equivalent

f = a(b(c(f)))

Zhijun liu first published in Zhihu column Python Zen, original address: https://zhuanlan.zhihu.com/p/24900548

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.