首页 > 技术文章 > 装饰器

GumpYan 2020-02-10 11:06 原文

1.装饰器的特点:
1.函数A作为参数出现的(函数B就接收函数A作为参数)
2.要有闭包的特点
 1 """
 2 装饰器的特点:
 3     1.函数A作为参数出现的(函数B就接收函数A作为参数)
 4     2.要有闭包的特点
 5 """
 6 
 7 
 8 # 可以将函数作为参数传进另一个函数里面去
 9 def funcA():
10     print("----->test")
11 
12 
13 def funcB(f):
14     print(f)
15     f()
16     print("------->func")
17 
18 
19 funcB(funcA)

 

 1 # 定义一个装饰器
 2 def decorate(func):
 3     a = 10
 4 
 5     def wrapper():
 6         func()
 7         print("--------->刷漆")
 8         print("--------->铺地板", a)
 9         print("--------->装门")
10     return wrapper
11 
12 
13 # 使用装饰器
14 @decorate
15 def house():
16     print("我是毛坯房")
17 
18 
19 # 调用函数
20 house()

 

 整个装饰器的流程

1.house被装饰函数

2.将被装饰的函数作为参数传递给装饰器decorator

3.执行decorate函数

4.将返回值又赋值给house,所以此时的house已经变成wrapper了

# 定义一个装饰器
def decorate(func):
    a = 10
    print("wrapper外层打印测试")

    def wrapper():
        func()
        print("--------->刷漆")
        print("--------->铺地板", a)
        print("--------->装门")

    print("wrapper加载完成")
    return wrapper


# 使用装饰器
@decorate
def house():
    print("我是毛坯房")


# 调用函数
house()
print(house)

 

简而言之:@a 就是将 b 传递给 a(),并返回新的 b = a(b)

例子:

上面使用@dobi来表示装饰器,其等同于:qinfeng = dobi(qinfeng)
因此装饰器本质上就是个语法糖,其作用为简化代码,以提高代码可读性,运行上段代码的结果为:

解析过程是这样子的:
(1)python 解释器发现@dobi,就去调用与其对应的函数( dobi 函数)
(2)dobi 函数调用前要指定一个参数,传入的就是@dobi下面修饰的函数,也就是 qinfeng()
(3)dobi() 函数执行,调用 qinfeng(),qinfeng() 打印“dobi”

 

2.装饰器加入可变参数和关键字

一个*,会把输入参数组成一个tuple

两个*,会把输入参数打包成字典

 1 import time
 2 
 3 
 4 def decorate(func):
 5     def wrapper(*args, **kwargs):   # {"clazz":'1904'}
 6         print("正在校验中.....")
 7         time.sleep(2)
 8         print("校验完毕.....")
 9         # 调用原函数
10         func(*args, **kwargs)
11 
12     return wrapper
13 
14 
15 @decorate
16 def f1(n):
17     print("----f1-----", n)
18 
19 
20 @decorate
21 def f2(name, age):
22     print("-----f2-----", name, age)
23 
24 
25 @decorate
26 def f3(students, clazz='1905'):
27     print("{}班级的学生如下:".format(clazz))
28     for stu in students:
29         print(stu)
30 
31 
32 
33 f1(5)
34 f2("tom", 20)
35 students = ["ketty", "tony", "rose"]
36 f3(students, clazz='1904')

 

3.多层装饰器

如果装饰器是多层的,谁距离函数最近,就优先使用那个,

多个装饰器装饰一个函数时,执行时的顺序是:最先装饰的装饰器,最后一个执行。它遵循了先进后出规则 类似于stack

 1 """
 2 如果装饰器是多层的,谁距离函数最近,就优先使用那个
 3 """
 4 
 5 
 6 def decorate1(func):
 7     print("--------> 1 start")
 8 
 9     def wrapper(*args, **kwargs):
10         func()
11         print("刷漆")
12     print("---------> 1 end")
13 
14     return wrapper
15 
16 
17 def decorate2(func):
18     print("--------> 2 start")
19 
20     def wrapper(*args, **kwargs):
21         func()
22         print("铺地板")
23 
24     print("------------> 2 end")
25 
26     return wrapper
27 
28 
29 @decorate2
30 @decorate1
31 def house():
32     print("我是毛坯房")
33 
34 
35 house()

原理:

 

 def定义一个函数,只是在内存中开辟空间,给出地址,并没有执行

只有调用函数的时候,才去根据地址,进入执行

 

4.带参数的装饰器

 1 """
 2 装饰器带参数,共有三层函数
 3 最外层的函数负责接收装饰器参数
 4 里面的内容还是原来的装饰器的内容
 5 """
 6 
 7 
 8 def outer(a):   # 第一层:负责接收装饰器的参数
 9     def decorate(func):  # 第二层:负责接收函数的
10         def wrapper(*args, **kwargs):  # 第三层:负责接收函数的参数
11             func(*args)
12             print("------->铺地砖{}块".format(a))
13         return wrapper  # 返出来的是第三层的函数名
14     return decorate   # 返出来的是第二层的函数名
15 
16 
17 @outer(a=10)
18 def house(time):
19     print("我是{}拿到房子钥匙的,还是毛坯房......".format(time))
20 
21 
22 @outer(100)
23 def street():
24     print("新修的街道名字是:春熙路")
25 
26 
27 house("2019-6-10")
28 street()

 5.装饰器与闭包的关系

事实上,装饰器就是一种的闭包的应用,只不过其传递的是函数

#装饰器
def outer(f):
    def inner(*args,**kargs):
        inner.co+=1
        return f(*args,**kargs)

    inner.co=0
    return inner

@outer
def f():
    pass

f()
f()
f()
print(f.co)

  运行结果:3

 

推荐阅读