Python's decorator detailed

Source: Internet
Author: User
Tags wrapper python decorator

Python's adorner use is a very important part of the Python language, which is the embodiment of the decorative pattern in the programming pattern, and Python provides a special syntactic sugar that can be easily implemented in decorative mode.

Introduction to Decorative Patterns
    • What do you mean, decorations? simply to add an extra attribute, method, or piece of code to an existing object to implement a new function without altering the structure of the original object or context , the implication is that the program's design conforms to the open-closed principle, which is open to expansion, but to the modification of the enclosing In other words, if a class or method is defined and not modified, you can extend its function by inheriting, decorating, acting and so on, and the ultimate goal is to reduce the coupling and ensure the stability of the system.
Simple implementation of Python decoration mode
class Car(object):    """    一个汽车类    """    def __init__(self):        self.logo = "奔驰" # 车标        self.oil = 2 # 油耗        self.ornamental = None # 装饰品    # 安装空调    def air_conditioner(self):        print("空调真舒服!")    def decorator(self, component):        """用来额外添加装饰方法的"""        self.ornamental = component# 由于汽车的装饰品会发生变化class Cushion(object):    """    坐垫    """    def __init__(self):        self.name = "席梦思"class Flower(object):    """    装饰花    """    def __init__(self, name):        self.name = nameif __name__ == '__main__':    car = Car()    cushion = Cushion()    flower = Flower("玫瑰")    # 汽车添加一个坐垫    car.decorator(cushion)    print(car.ornamental)    # 添加一个花    car.decorator(flower)    print(car.ornamental)

In the example above, the cushions and flowers are additional properties that can be added dynamically to the car so that the structure of the car class can be streamlined and the car class can be extended.

Python's Decorator
    • Python decorator is mainly for a completed method to add additional code to execute, in order to maintain the original method is not changed, the adorner should return a new method after decoration, the implementation of this function requires a carrier, the vector can be a function can also be a class, At the same time Python provides syntax sugar @ To complete the decoration function.
Introduction to the implementation principle of Python decorator
@decoratordef get_name():    pass

As shown above, when the current code initializes the loading context, define the Get_name function, decorator must be loaded into the context before the Get_name function is defined, and the interpreter encounters the syntax sugar at @, which will get_ The name function executes the decorator as a parameter in decorator and returns a new method that assigns the returned method to Get_name.

What is the structure of the decorator? We can look at the adorner requirements and need to return a new method, so it can be a closure structure or a class structure.

Adorners using a closure structure
def decorator(func):    def new_func(*args,**kwargs):        "do something"        res = func(*args,**kwargs)        "do something"        return res    return new_func@decoratordef get_name():    pass

What is a closure package? A new function is defined in the function, which uses the variables of the outer function, which is called the closure of the function and some of the variables used. The closure function is not defined when the context is initialized and is defined only when it is executed.

As shown in the example above, the Get_name function is passed as a parameter to the Decorator function, and the parameter that executes the decorator function returns the New_func,new_func function is the parameter of the Get_name function, New_func assigns to Get_name. Additional code is added to the Get_name method at this time.

    • Adorner with parameters

If you need to add additional parameters to the original adorner, you must use a double-layer closure structure.

def new_decorator(pas=""):    def decorator(func):        def new_func(*args,**kwargs):            "do something"            print(pas)            res = func(*args,**kwargs)            "do something"            return res        return new_func    return decorator@new_decorator('new prams')def get_name():    pass

As shown above, the PAS parameter is passed to the closure function, when the context is loaded, new_decorator due to the () Form, will be directly executed, return the inner layer decorator function, and then the normal way to decorate the process of execution, The PAS parameter, which is referenced by the inner layer function, will reside in memory for a long time without being freed.

    • Multi-layer Decoration

If you add additional code functionality to a function that has already been decorated, you need to add the adorner again, and the focus is on the order in which the adorner functions are executed.

def new_decorator(pas=""):    def decorator(func):        def new_func(*args,**kwargs):            "do something"            print(pas)            res = func(*args,**kwargs)            "do something"            return res        return new_func    return decoratordef tow_decorator(func):    def new_func(*args, **kwargs):        res = func(*args, **kwargs)        "do other something"        return res    return new_func@tow_decorator@new_decorator(pas='hhhhh')def get_name():

The order of the adorner functions is different from our custom, according to the nearest principle, that is, the New_decorator will first perform the decoration, assign the returned function to Get_name, and then go up, so the overall structure of the adornment code is TOW_ Decorator adds extra code that wraps New_decorator code, which is important when you need to consider the order in which the code is executed.

Adorners that use class structure
class Decorator(object):    def __init__(self, func):        self.func = func    def __call__(self, *args, **kwargs):        """        添加额外的代码        :param args:        :param kwargs:        :return:        """        print('do something')        return self.func(*args, **kwargs)@Decoratordef get_name():    passprint(isinstance(get_name, Decorator))# 结果True

As above, as in Python, as long as an object with the __call__ method, you can use the form of XXX () to invoke the code in the call method, the use of this principle we can implement the structure of the adorner;

When the context is initialized, execution Decorator,get_name initializes the instance of decorator as a parameter, returning an instance of decorator to Get_name, when Get_name is an instance object of a class when called get_ Name executes the call method of the decorator instance object. But this approach is a bit more complicated than the closure structure, generally not.

    • Adorner with parametric structure

What if you need to add extra parameters to use the class structure? As with the closure structure, add one more layer.

def three_decorator(pas=''):    class Decorator(object):        def __init__(self, func):            self.func = func            self.pas = pas        def __call__(self, *args, **kwargs):            """            添加额外的代码            :param args:            :param kwargs:            :return:            """            print('do something')            return self.func(*args, **kwargs)    return Decorator@three_decorator('hhhhh')def get_name():    pass
Side effects of using adorners

The use of adorners does not need to say, function check, enter the log, function execution before and after the processing of many scenes are needed, it also has a little side effects.

def tow_decorator(func):    def new_func(*args, **kwargs):        """new_func function"""        res = func(*args, **kwargs)        print("do other something")        return res    return new_func@tow_decoratordef get_name():    """get_name function"""    print('jjjjjj')if __name__ == '__main__':    import inspect    print(inspect.signature(get_name))    x = inspect.getsource(get_name)    print(x)    print(get_name.__doc__)# 结果(*args, **kwargs) # 函数签名    def new_func(*args, **kwargs): # 函数源码        """new_func function"""        res = func(*args, **kwargs)        print("do other something")        return res‘new_func function’ # 函数文档

As you can see, because get_name is decorated and points to the New_func function, the information obtained is no longer a description of the Get_name function, which is inconvenient for debugging. We can basically eliminate this side effect by using the wraps function of the Functools module.

def tow_decorator(func):    @functools.wraps(func)    def new_func(*args, **kwargs):        """new_func function"""        res = func(*args, **kwargs)        print("do other something")        return res    return new_func@tow_decoratordef get_name():    """get_name function"""    print('jjjjjj')if __name__ == '__main__':    print(get_name.__doc__)# 结果()@tow_decoratordef get_name():    """get_name function"""    print('jjjjjj')get_name function
General ways to create adorners
    • Closure structure mode

According to our above analysis, the standard adorner structure:

import functoolsdef decorator(func):    @functools.wraps(func)    def new_func(*args,**kwargs):        "do something"        res = func(*args,**kwargs)        print("do something")        return res    return new_func
    • Using the decorator and decorate of the decorator.py module

The decoration function of the closure structure is not very intuitive, we can use the third party package class to improve the situation, so that the adorner function is more readable.

from decorator import decorator, decorate, contextmanagerdef wrapper(func, *args, **kwargs):    """装饰函数的内容"""    res = func(*args, **kwargs)    print("do other something")    return resdef one_decorator(func):    """进行装饰"""    return decorate(func, wrapper)@one_decoratordef get_name(pas='fff'):    """get_name function"""    print('jjjjjj')if __name__ == '__main__':    import inspect    print(inspect.signature(get_name))    x = inspect.getsource(get_name)    print(x)    print(get_name.__doc__)    get_name()

The new function is written separately, so that the original closure structure into two functions, code readability is better, you can find decorate to help us deal with the side effect of the adorner, but an adorner needs to define two functions a bit troublesome, to more elegant use of decorator.

from decorator import decorator, decorate, contextmanager@decoratordef five_decorator(func, *args, **kwargs):    res = func(*args, **kwargs)    print("do other something")    return res@five_decoratordef get_name(pas='fff'):    """get_name function"""    print('jjjjjj')if __name__ == '__main__':    import inspect    print(inspect.signature(get_name))    x = inspect.getsource(get_name)    print(x)    print(get_name.__doc__)    get_name()

Now the adorner becomes the definition of a function, simple and intuitive, while also dealing with side effects, is an ideal way to create an adorner.

    • Using the decorator of the WRAPT module
import wrapt@wrapt.decoratordef six_decorator(func, instance, args, kwargs):    res = func(*args, **kwargs)    print(instance)    print("do other something")    return res@six_decoratordef get_name(pas='fff'):    """get_name function"""    print('jjjjjj')if __name__ == '__main__':    import inspect    print(inspect.signature(get_name))    x = inspect.getsource(get_name)    print(x)    print(get_name.__doc__)    get_name()

The decorator using the Wrapt module can also be intuitively implemented, but the adorner's parameters func, instance, args, Kwargs are fixed; args, the Kwargs parameter is not preceded by an * number, Instance can get an instance object of the class when decorating the class method, and Func is the decorated function.

    • Tianyu tour
    • Source:/http www.cnblogs.com/cwp-bg/
    • This article is copyright to the author and the blog Park, welcome, exchange, but without the consent of the author must retain this statement, and in the article obvious location to give the original link.

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.