• python装饰器笔记


    装饰器本质是一个函数,装饰器装饰一个函数,其实就是返回另一个函数,强调装饰器本质是函数以及返回的是函数,这个比较重要,因为装饰器语法看起来挺唬人的,其实也就是把对函数的操作绕了两三下,没啥高大上的内容

    1、多层装饰器:

    def makeBold(fun):
        print("***** 1 *******")
        def wrapped():
            print("------ 1 ------")
            return "<b> " + fun() + " <\b>"
        return wrapped
    
    
    def makeItalic(fun):
        print("***** 2 *******")
        def wrapped():
            print("------- 2 ------")
            return "<i> " + fun() + " <i>"
        return wrapped
    
    
    @makeBold
    @makeItalic
    def fun():
        return "hello world"
    
    
    print(fun())
    
    输出:
    ***** 2 *******
    ***** 1 *******
    ------ 1 ------
    ------- 2 ------
    <b> <i> hello world <i> <>

    由此可见,解释器解释过程为:执行到@makeBold时,发现底下仍然是个装饰器,于是跳过先执行@makeItalic,等完成@makeItalic对fun的装饰(结果是返回一个函数),再返过去用@makeBold装饰第一步返回的函数

    2、带参数的装饰器

    带参数的装饰器其实就是一个装饰器“生成器”,或者说装饰器工厂,他返回的是一个常规的生成器,用返回的这个装饰器去装饰待装饰的函数。

    比如functools.wraps就是一个带参数的装饰器。下面记录下对functools.wraps的原理理解。

    一般装饰器会改变函数的一些属性,比如:

    def log(func):
        def log_wrap(*args, **kargs):
            """this is log_wrap"""
            return func(*args, **kargs)
        return log_wrap
    
    
    @log
    def func():
        """print a string"""
        pass
    
    
    print(func.__name__)
    print(func.__doc__)

    输出:

    log_wrap
    this is log_wrap

    这是因为func函数经@log装饰后,函数名func已经指向了log_wrap这个函数,所以原来的那个func的属性都丢失了,取而代之的是log_wrap的各个属性,但是实际应用中这是我们不愿看到的,因为一个函数经装饰后,不仅要看起来功能增强了,而且还要看起来函数还是原来的那个函数,即要保持原来函数的属性不变,要实现这个功能,可以做如下修改:

    def log(func):
        def log_wrap(*args, **kargs):
            """this is log_wrap"""
            return func(*args, **kargs)
        log_wrap.__name__ = func.__name__
        log_wrap.__doc__ = func.__doc__
        return log_wrap
    
    
    @log
    def func():
        """print a string"""
        pass
    
    输出:
    func
    print a string

    这下就对了,装饰前后,原函数的属性都是一样的。python提供了functools.wraps这个装饰器来替我们完成这件事,只需要如下使用,即可保证被装饰的函数属性不变:

    from functools import wraps
    
    def log(func):
        @wraps(func)  # 在这里加一个装饰器即可保证原函数的属性不变
        def log_wrap(*args, **kargs):
            """this is log_wrap"""
            return func(*args, **kargs)
        return log_wrap
    
    
    @log
    def func():
        """print a string"""
        pass
    
    
    print(func.__name__)
    print(func.__doc__)
    
    输出:
    func
    print a string
    wraps装饰器的源码没细看了,根据它的作用,以及它是个带参数的装饰器,所以自己简单实现了一下这个装饰器:
    def wraps(f):
        def wrapper(func):
            def inner(*args, **kwargs):
                return func(*args, **kwargs)
            inner.__name__ = f.__name__
            inner.__doc__ = f.__doc__
            return inner
        return wrapper
    
    
    def log(func):
        @wraps(func)  # 返回一个装饰器,用返回的装饰器装饰log_wrap,还是返回一个函数
        def log_wrap(*args, **kargs):
            """this is log_wrap"""
            return func(*args, **kargs)
        return log_wrap
    
    
    @log
    def func():
        """print a string"""
        pass
    
    
    print(func.__name__)
    print(func.__doc__)
    
    输出:
    func
    print a string

    效果是一样的。

    @wraps(func)这句其实返回的是一个装饰器,用这个返回的装饰器装饰log_wrap,返回的还是一个函数
  • 相关阅读:
    BZOJ 4726: [POI2017]Sabota? 树形dp
    Codeforces Round #381 (Div. 1) B. Alyona and a tree dfs序 二分 前缀和
    uestc_retarded 模板
    CROC 2016
    Codeforces Round #381 (Div. 1) A. Alyona and mex 构造
    BZOJ 2648: SJY摆棋子 kdtree
    BZOJ 3732: Network 最小生成树 倍增
    HDU 5914 Triangle 数学找规律
    HDU 5902 GCD is Funny 数学
    Codeforces Round #379 (Div. 2) E. Anton and Tree 缩点 直径
  • 原文地址:https://www.cnblogs.com/olivertian/p/12215951.html
Copyright © 2020-2023  润新知