装饰器 python

定义:在不修改一个函数内部代码的状况下,给函数添加扩展功能,即只能在函数以前或者以后添加功能,不能在函数内部添加,常见的应用场景如log日志、权限检验等。

1.不带参数的装饰器

  1. 无参数无返回值得函数

    # 定义一个装饰器
    def set_func(func):
        def call_func():
            print('————在函数以前添加功能——————')
            func()
            print('————在函数后面添加功能——————')
        return call_func
    
    # 定义一个函数,并添加装饰器
    @set_func    # 等价于 func1=set_func(func1)
    def func1():
        print('____func1____')
    
    # 调用函数
    func1()复制代码

  • 代码执行结果
    ————在函数以前添加功能——————
    ____func1____
    ————在函数后面添加功能——————
    
    复制代码

2. 有参数有返回值得函数
python

# 定义装饰器
def set_func(func):
    def call_func(*args, **kwargs):
        print('——————添加功能————————')
        return func(*args, **kwargs)
    return call_func


# 定义一个函数并进行装饰
@set_func
def func1(m):
    print('____func1____')
    return m

# 调用函数
f = func1(100)
print(f)复制代码
  • 代码执行结果

——————添加功能————————
____func1____
100复制代码
2.带有参数的装饰器

import time


# 定义装饰器
def set_log(log):
    def set_func(func):
        # 定义log_dict 字典
        log_dict = {1: 'error', 2: 'warning'}
        def call_func(*args, **kwargs):
            # 打开log.txt文件(没有就建立文件),追加内容
            with open('log.txt', 'a', encoding='utf-8') as f:
                f.write('%s ---%s---调用了函数%s\n' % (log_dict[log], str(time.ctime()), func.__name__))
            return func(*args, **kwargs)
        return call_func
    return set_func


# 定义一个函数并进行装饰
@set_log(1)
def func1(m):
    print('____func1____')
    return m

# 调用函数
f = func1(100)
print(f)复制代码

  • 代码执行结果

____func1____
100

# log.txt 文件内容
error ---Fri Jan  5 22:01:24 2018---调用了函数func1复制代码

3.一个函数有两个装饰器缓存

# 定义装饰器1
def set_log(func):
    print('————开始装饰sel_log————')

    def call_func():
        print('___set_log___')
        func()
    return call_func


# 定义装饰器2
def set_func(func):
    print('————开始装饰set_func————')

    def call_func():
        print('___set_func___')
        func()
    return call_func


# 定义函数,并添加装饰器
@set_log
@set_func    # 等价于 func1=set_func(func1)
def func1():
    print('____func1____')复制代码

  • 先不调用函数运行代码,会看到以下结果

    ————开始装饰set_func————
    ————开始装饰sel_log————
    复制代码

  • 调用函数,再看执行结果

    # 调用函数
    func1()复制代码

# 执行结果

————开始装饰set_func————
————开始装饰sel_log————
___set_log___
___set_func___
____func1____复制代码
  • 解释说明:
  1. 装饰器在代码写完后,此时不用调用函数,就会进行装饰
  2. 函数装饰时,会先执行离函数最近的装饰器
  3. 当调用函数时,函数会先执行离函数最远的装饰器再执行离的近的装饰器,再执行函数自己

4. 1、functools.wrapsbash

import time
import functools

# 定义装饰器
def clock(func):
    @functools.wraps(func)
    # 使用 functools.wraps装饰器把相关的属性从 func复制到 clocked 中,避免遮盖了被装饰函数的 __name__ 和 __doc__ 属性
    def clocked(*args, **kwargs):
        t0 = time.perf_counter()
        result = func(*args, **kwargs)
        elapsed = time.perf_counter() - t0
        # 当前执行的函数名称
        name = func.__name__
        arg_lst = []
        if args:
            arg_lst.append(', '.join(repr(arg) for arg in args))
        if kwargs:
            pairs = ['%s=%r' % (k, w) for k, w in sorted(kwargs.items())]
            arg_lst.append(', '.join(pairs))
        arg_str = ', '.join(arg_lst)
        print('[%0.8fs] %s(%s) -> %r ' % (elapsed, name, arg_str, result))

        return result

    return clocked复制代码

2、使用functools.lru_cache作备忘app

functools.lru_cache 是很是实用的装饰器,它实现了备忘(memoization)功能。这是一 项优化技术,它把耗时的函数的结果保存起来,避免传入相同的参数时重复计算,代表缓存不会无限制增加,一段时间不用的缓存 条目会被扔掉。
函数

# 用上一个装饰器clock
@clock
def fib(n):
    if n < 2:
        return 1
    return fib(n-2) + fib(n-1)


if __name__ == '__main__':
    fib(5)

# 执行结果
[0.00000000s] fib(1) -> 1 
[0.00000000s] fib(0) -> 1 
[0.00000000s] fib(1) -> 1 
[0.00000000s] fib(2) -> 2 
[0.00000000s] fib(3) -> 3 
[0.00000000s] fib(0) -> 1 
[0.00000000s] fib(1) -> 1 
[0.00000000s] fib(2) -> 2 
[0.00000000s] fib(1) -> 1 
[0.00000000s] fib(0) -> 1 
[0.00000000s] fib(1) -> 1 
[0.00000000s] fib(2) -> 2 
[0.00000000s] fib(3) -> 3 
[0.00000000s] fib(4) -> 5 
[0.00000000s] fib(5) -> 8 

Process finished with exit code 0

复制代码

# 用functools.lru_cache进行装饰
@functools.lru_cache()
@clock
def fib(n):
    if n < 2:
        return 1
    return fib(n-2) + fib(n-1)if __name__ == '__main__':
    fib(5)# 执行结果
[0.00000000s] fib(1) -> 1 
[0.00000000s] fib(0) -> 1 
[0.00000000s] fib(2) -> 2 
[0.00100088s] fib(3) -> 3 
[0.00000000s] fib(4) -> 5 
[0.00100088s] fib(5) -> 8 
复制代码

3、参数选择functools.lru_cache(maxsize=128, typed=False)
性能

maxsize 参数指定存储多少个调用的结果。缓存满了以后,旧的结果会被扔掉,腾出空间。 为了获得最佳性能,maxsize 应该设为 2 的幂。typed 参数若是设为 True,把不一样参数类型 获得的结果分开保存,即把一般认为相等的浮点数和整数参数(如 1 和 1.0)区分开。顺 便说一下,由于 lru_cache 使用字典存储结果,并且键根据调用时传入的定位参数和关键 字参数建立,因此被 lru_cache 装饰的函数,它的全部参数都必须是可散列的。
优化

4、单分派泛函数singledispatchui

能够把总体方案拆成多个模块,甚至能够为你没法修改的类提供专门的函数,使用@singledispatch装饰的函数会变成泛函数spa

  • 一、singledispatch:标记处理object类型的基函数 
  • 二、各个专门函数使用@<<base_function>>.register(<<type>>)装饰 
  • 三、专门函数的名称可有可无,_是个不错的选择,简单明了 
  • 四、为每一个须要处理的类型注册一个函数 
  • 五、能够叠放多个register装饰器,让同一个函数支持不一样类型

from functools import singledispatch

@singledispatch
def show(obj):
    print(obj, type(obj), "obj")


# 参数字符串
@show.register(str)
def _(text):
    print(text, type(text), "str")


# 参数int
@show.register(int)
def _(n):
    print(n, type(n), "int")


# 参数元祖或者字典都可
@show.register(tuple)
@show.register(dict)
def _(tup_dic):
    print(tup_dic, type(tup_dic), "int")


if __name__ == '__main__':
    show(1)
    show("xx")
    show([1])
    show((1, 2, 3))
    show({"a": "b"})

# 执行结果
1 <class 'int'> int
xx <class 'str'> str
[1] <class 'list'> obj
(1, 2, 3) <class 'tuple'> int
{'a': 'b'} <class 'dict'> int
复制代码
相关文章
相关标签/搜索