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> <\b>

由此可见,解释器解释过程为:执行到@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,返回的还是一个函数
posted @ 2020-01-19 23:11  olivertian  阅读(276)  评论(0编辑  收藏  举报