装饰器概念

一个简单的语法糖,或者说是一个特殊的高阶函数.

通过保证目标函数来修改其行为, 绝大多数装饰器 都是通过闭包的原理 来实现的.

 @cache
 def function():....

 等同于

 def function():...
 function = cache(function)

无参数的装饰器

"""
没有参数的装饰器
功能: 打印包装函数的执行时间
"""
import time


def timer(func):
    def decorated(*args, **kwargs):
        st = time.perf_counter()
        ret = func(*args, **kwargs)
        print("cost time", time.perf_counter() - st)
        return ret

    return decorated


@timer
def sleep_func():
    time.sleep(1)


if __name__ == '__main__':
    sleep_func()

有参数的装饰器

给刚才的timer装饰器,加上print_agrs参数,是否打印参数

"""
没有参数的装饰器
功能: 打印包装函数的执行时间
"""
import time


def timer(print_args=False):
    def decorated(func):
        def wrapper(*args, **kwargs):
            st = time.perf_counter()
            ret = func(*args, **kwargs)
            if print_args:
                print("调用参数", args, kwargs)
            print("cost time", time.perf_counter() - st)
            return ret

        return wrapper

    # 带参数,外层其实是个保证,返回装饰器函数
    return decorated


@timer(print_args=True)
def sleep_func(port, ip="127.0.0.1"):
    """文档注释"""
    time.sleep(1)



if __name__ == '__main__':
    sleep_func(8888)
    print(sleep_func.__name__)
    help(sleep_func)
    # 这是因为装饰器再包装目标函数的时候,丢失了函数的元信息,是一种副作用
    # 函数名字和help信息都丢了 functools.wraps 可以


"""
相当于
_decorated = timer(print_args=True)
sleep_func = _decorated(sleep_func)
或
sleep_func = timer(print_args=True)(sleep_func)
有参数的装饰器明显难写了不少, 可以看到, 我们比无参数的装饰器多调用了一次,实现代码也多了一层函数嵌套.
后面我们会用类来实现有参数的装饰器,可以明显较少代码嵌套的层数
"""

使用functools.wraps()修饰包装函数

import time
from functools import wraps


def timer(print_args=False):
    def decorated(func):

        # 新增到这,加到被装饰的函数
        @wraps(func)
        def wrapper(*args, **kwargs):
            st = time.perf_counter()
            ret = func(*args, **kwargs)
            if print_args:
                print("调用参数", args, kwargs)
            print("cost time", time.perf_counter() - st)
            return ret

        return wrapper

    return decorated


@timer(print_args=True)
def sleep_func(port, ip="127.0.0.1"):
    """文档注释"""
    time.sleep(1)


if __name__ == '__main__':
    sleep_func(8888)
    print(sleep_func.__name__)
    help(sleep_func)

类装饰器

类装饰器,主要运用__call__可调用对象这个特性

import time
from functools import wraps


class Timer:
    def __init__(self, print_args):
        self.print_args = print_args

    def __call__(self, func):
        @wraps(func)
        def decorated(*args, **kwargs):
            s = time.time()
            ret = func(*args, **kwargs)
            if self.print_args:
                print(ret)
            print(time.time() - s)
            return ret

        return decorated


@Timer(False)
def hello():
    time.sleep(1)
    return "www"


if __name__ == '__main__':
    hello()

另一种方式实现类装饰器

import time
from functools import wraps, update_wrapper


class Timer:
    def __init__(self, func):
        update_wrapper(self, func)
        self.func = func

    def __call__(self, *args, **kwargs):
        s = time.time()
        ret = self.func(*args, **kwargs)
        print(time.time() - s)
        return ret


@Timer
def hello():
    time.sleep(1)
    return "www"


if __name__ == '__main__':
    hello()

再加一种

import time
from functools import wraps, update_wrapper, partial


class Timer:
    def __init__(self, func, *, args1=None):
        update_wrapper(self, func)
        self.func = func
        self.args1 = args1

    def __call__(self, *args, **kwargs):
        s = time.time()
        ret = self.func(*args, **kwargs)
        print(time.time() - s)
        print(self.args1)
        return ret


def delayed_start(**kwargs):
    return partial(Timer, **kwargs)


@delayed_start(args1="ccc")
def hello():
    time.sleep(1)
    return "www"


if __name__ == '__main__':
    hello()

results matching ""

    No results matching ""