基本概念

语法糖

介绍

  1. 语法糖是指计算机语言中的某种特殊语法,该语法对语言的功能没有任何影响
  2. 语法糖不会增加或减少任何功能,它只是对程序更便捷、易读的一种写法
  3. 语法糖可以完全等价地转换为非原本语法糖的代码

代码示例

语法糖在不影响代码功能的情况下对初始代码进行一种更便捷的写法

1
2
3
4
5
6
7
8
9
# 初始代码
users = []
for i in range(1,10):
if i % 2 == 0:
users.append(i)
print(users)

# 结果
[2, 4, 6, 8]
1
2
3
4
5
6
# 语法糖
users = [i for i in range(1,10) if i % 2 == 0]
print(users)

# 结果
[2, 4, 6, 8]

闭包

介绍

  1. 在内部函数中对外部作用域的变量进行引用,并且一般外部函数的返回值为内部函数,那么内部函数就被称之为闭包,外部函数就被称为闭包生成函数
  2. 闭包中不能修改外部函数的局部变量

语法

1
2
3
4
5
6
# 在外部函数中定义了一个内部函数,内部函数访问了外部函数的变量,并且外部函数返回值为内部函数

def 外部函数(x):
def 内部函数(y):
return x + y
return 内部函数

装饰器

介绍

  1. 装饰器的本质就是闭包
  2. 装饰器是对闭包的一种语法糖
  3. 装饰器通常用于装饰函数、类的方法
  4. 一般被装饰后的函数会在原来的基础上增加一些新功能

代码示例

被装饰的函数 = 外部函数(被装饰的函数)等同于@外部函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 闭包
def 外部函数(func):
def 内部函数(*args,**kwargs):
# 调用前
result = func(*args,**kwargs)
# 调用后
return result
return 内部函数

def 被装饰的函数():
代码块

被装饰的函数 = 外部函数(被装饰的函数)
print(被装饰的函数())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 装饰器
def 外部函数(func):
def 内部函数(*args,**kwargs):
# 调用前
result = func(*args,**kwargs)
# 调用后
return result
return 内部函数

@外部函数
def 被装饰的函数():
代码块

print(被装饰的函数())

基本知识

一切皆对象

在函数名后面加上()后函数会被执行,如test()。反之不加()则函数不会被执行,如test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def test():
print("test")
return "我是test()函数"

print("第一次输出: " + str(test)) # 不加(),函数没有被执行
print("----------------")
print("第二次输出: " + str(test())) # 加上(),函数被执行
print("----------------")

a = test # 将函数赋值给变量a,函数不会被执行
print("第三次输出: " + a()) # 执行函数

# 结果
第一次输出: <function test at 0x000001E1DC7E0F78>
----------------
test
第二次输出: 我是test()函数
----------------
test
第三次输出: 我是test()函数

在函数中定义函数

Python可以在函数的代码块中定义一个或多个函数,外层的函数被称为外部函数,内层的函数被称为内部函数,内部函数不能在外部函数外被调用。

下方代码中外部函数为test,内部函数有func_1func_2,在外部函数的代码块中使用print调用内部函数,在函数外的代码中使用test()调用外部函数,则内外函数都会被执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def test():
print("我是test()函数")

def func_1():
return "我是test函数下的func_1函数"

def func_2():
return "我是test函数下的func_2函数"

print(func_1()) # 调用func_1
print(func_2()) # 调用func_2

test() # 调用test函数

# 结果
我是test()函数
我是test函数下的func_1函数
我是test函数下的func_2函数

# 调用内部函数是时报错
func_1()
NameError: name 'func_1' is not defined

从函数中返回函数

在函数中定义函数时,并不一定要在外部函数中执行内部函数,也可以将内部函数作为外部函数的返回值进行返回

下方代码中使用传参的方式来指定要返回的函数,要注意返回的是func_1而不是func_1()。后者在test函数被赋值调用时会被执行,而前者只会被传递而不会被执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
def test(name="func_1"):
print("我是test()函数")

def func_1():
return "我是test函数下的func_1函数"

def func_2():
return "我是test函数下的func_2函数"

if name == "func_1":
return func_1 # 注意: 返回的是func_1而不是func_1()
else:
return func_2 # 同上

# 调用函数test,函数test会被执行,而形参name的默认值为"func_1",所以返回func_1函数,func_1函数不会被执行
a = test()
print(a)
"""
结果:
我是test()函数
<function test.<locals>.func_1 at 0x000001CF7D57D558>
"""

# 调用函数test,函数test会被执行,传参后test函数会返回func_2函数,func_2函数不会被执行
b = test(name="func_2")
print("函数test返回的数据类型为: " + str(type(b)))
print(b)
"""
结果:
我是test()函数
函数test返回的数据类型为: <class 'function'>
<function test.<locals>.func_2 at 0x000001CF7D57DF78>
"""

# test函数被执行后返回func_1函数并将该函数赋值给变量a,这时候再执行变量a时就等同于执行func_1函数
print(a())
"""
结果:
我是test函数下的func_1函数
"""

将函数作为参数传递给另一个函数

将函数A作为参数传递给函数B,在函数B中执行函数A,并且可以将函数A的返回结果进行再次运算或修饰、直接输出、返回等操作,而该操作其实就是闭包

下方代码中func_2(func_1)将函数func_1传递给函数func_2,函数func_2在传参时会被执行。而函数func_1是在函数func_2内部的print(func())中被执行的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def func_1():
print("当我被执行时我会率先打印")
return "我是函数func_1"

def func_2(func):
print("我是函数func_2")
print(func()) # 执行传递进来的函数并打印返回值

# 将函数1传递给函数2,函数1是被函数2执行的,而不是传参时被执行的
func_2(func_1)

# 结果
我是函数func_2
当我被执行时我会率先打印
我是函数func_1

下方代码中func_2(func_1())将函数func_1执行后再将执行结果传参给函数func_2,这里要注意实参是func_1()而不是func_1,所以会先执行func_1再执行func_2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def func_1():
print("当我被执行时我会率先打印")
return "我是函数func_1"

def func_2(func):
print("我是函数func_2")
print(func) # 执行传递进来的函数

# 将函数1传递给函数2,函数1在传递时已经被执行了,所以会先打印函数1的代码块
func_2(func_1())

# 结果
当我被执行时我会率先打印
我是函数func_2
我是函数func_1

闭包

定义闭包

下方代码中定义了一个外部函数test,该函数运行时先打印我是外部函数test(),再返回内部函数func_1。而代码a=test(10)将10传参给函数test(),这时候函数test会被执行且打印我是外部函数test(),并将返回值赋值给变量a,这时候变量a就等同于内部函数func_1,再对变量a传参时就等同于执行函数func_1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def test(x):
print("我是外部函数test()")

def func_1(y):
return x + y

return func_1


a = test(10) # 调用外部函数test并传参,外部函数返回值为内部函数(一整个函数),现在变量a就是内部函数
"""
结果:
我是外部函数test() # 外部函数被执行
"""

print(a)
"""
结果:
<function test.<locals>.func_1 at 0x000001C07D3C0EE8> # 外部函数将内部函数作为返回值返回
"""

print(a(20))
"""
现在变量a就是内部函数func_1,对内部函数进行传参后内部函数返回相加后的结果
结果:
30
"""

使用闭包

未使用闭包时

想要知道某个函数的运行时间时,就要在每个函数内重复的计算时间,这会使得代码很冗余,代码量变多,如下示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import time

def func_1():
start = time.time() # 函数开始执行时间
time.sleep(0.2) # 模拟函数运行所耗费的时间
print("我是func_1函数")
end = time.time() # 函数结束执行时间
total_time = end - start # 函数运行所耗费的时间
return total_time

def func_2():
start = time.time() # 函数开始执行时间
time.sleep(0.2) # 模拟函数运行所耗费的时间
print("我是func_2函数")
end = time.time() # 函数结束执行时间
total_time = end - start # 函数运行所耗费的时间
return total_time

def func_3():
start = time.time() # 函数开始执行时间
time.sleep(0.2) # 模拟函数运行所耗费的时间
print("我是func_3函数")
end = time.time() # 函数结束执行时间
total_time = end - start # 函数运行所耗费的时间
return total_time

print(func_1())
print(func_2())
print(func_3())

# 结果
我是func_1函数
0.20311379432678223
我是func_2函数
0.20303797721862793
我是func_3函数
0.2140798568725586

使用闭包

下方代码中定义了一个外部函数get_runtime,在外部函数中定义了一个内部函数run,外部函数通过传参获取到需要运算执行时间的函数,内部函数负责运算外部函数通过传参进来的函数的执行时间,且返回该函数的名称与执行时间。内部函数作为外部函数的返回值被返回了,所以下方代码func_1 = get_runtime(func_1)中变量func_1就是内部函数run。代码print(func_1())就等同执行了内部函数run,内部函数中再执行了传递进来的函数func_1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import time

def get_runtime(func):
# 在函数get_runtime内定义函数run,但函数run不被执行,而是当作函数get_runtime的返回值返回了
def run():
start = time.time() # 函数开始执行时间
func() # 运行传递进来的函数
end = time.time() # 函数结束执行时间
total_time = end - start # 函数运行所耗费的时间
return f"函数{func.__name__}运行时间: {total_time}"
return run


def func_1():
time.sleep(0.2) # 模拟函数运行所耗费的时间
print("我是func_1函数")

def func_2():
time.sleep(0.2) # 模拟函数运行所耗费的时间
print("我是func_2函数")

def func_3():
time.sleep(0.2) # 模拟函数运行所耗费的时间
print("我是func_3函数")


# print(get_runtime(func_1)()) # 方法2
func_1 = get_runtime(func_1)
func_2 = get_runtime(func_2)
func_3 = get_runtime(func_3)

print(func_1())
print(func_2())
print(func_3())

# 结果
我是func_1函数
函数func_1运行时间: 0.20359444618225098
我是func_2函数
函数func_2运行时间: 0.2025609016418457
我是func_3函数
函数func_3运行时间: 0.20307469367980957

带返回值的闭包

上述例子中被装饰的函数都没有返回值,而下方被装饰的函数中使用返回值后却没有被返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import time

def get_runtime(func):
# 在函数get_runtime内定义函数run,但函数run不被执行,而是当作函数get_runtime的返回值返回了
def run():
start = time.time() # 函数开始执行时间
func() # 运行传递进来的函数
end = time.time() # 函数结束执行时间
total_time = end - start # 函数运行所耗费的时间
return f"函数{func.__name__}运行时间: {total_time}"
return run

def func_1():
time.sleep(0.2) # 模拟函数运行所耗费的时间
print("我是func_1函数")
return "Hello func_1" # 被装饰函数的返回值

func_1 = get_runtime(func_1)
print(func_1())

# 结果
我是func_1函数
函数func_1运行时间: 0.20908427238464355

在闭包中执行函数时可以将被装饰的函数的执行结果赋值给变量,再由内部函数返回该变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import time

def get_runtime(func):
# 在函数get_runtime内定义函数run,但函数run不被执行,而是当作函数get_runtime的返回值返回了
def run():
start = time.time() # 函数开始执行时间
response = func() # 运行传递进来的函数,并将该函数的返回值赋值给变量
end = time.time() # 函数结束执行时间
total_time = end - start # 函数运行所耗费的时间
print(f"函数{func.__name__}运行时间: {total_time}")
return response
return run

def func_1():
time.sleep(0.2) # 模拟函数运行所耗费的时间
print("我是func_1函数")
return "Hello func_1"

func_1 = get_runtime(func_1)
print(func_1())

# 结果
我是func_1函数
函数func_1运行时间: 0.21098947525024414
Hello func_1

带参数的闭包

外部函数负责接收需要被装饰的函数,内部函数负责接收需要被装饰的函数的参数。在内部函数用使用*args**kwargs来接收所有的参数(算固定格式),并将该参数传入到被装饰的函数中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import time

def get_runtime(func):
# 在函数get_runtime内定义函数run,但函数run不被执行,而是当作函数get_runtime的返回值返回了
def run(*args,**kwargs): # 接收函数参数
start = time.time() # 函数开始执行时间
result = func(*args,**kwargs) # 运行传递进来的函数,传入参数并将返回值赋值给变量result
end = time.time() # 函数结束执行时间
total_time = end - start # 函数运行所耗费的时间
print(f"函数{func.__name__}运行时间: {total_time}")
return result # 返回未增强函数的返回值
return run


def func_1(num1,num2):
time.sleep(0.2) # 模拟函数运行所耗费的时间
print("我是func_1函数")
return num1 + num2

func_1 = get_runtime(func_1)
print(func_1(1,1))

# 结果
我是func_1函数
函数func_1运行时间: 0.21248936653137207
2

装饰器

定义装饰器

在基本概念中了解到装饰器就是闭包的语法糖,所以定义装饰器就类似于闭包

闭包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import time

def get_runtime(func):

def run():
start = time.time() # 函数开始执行时间
func() # 运行传递进来的函数
end = time.time() # 函数结束执行时间
total_time = end - start # 函数运行所耗费的时间
return f"函数{func.__name__}运行时间: {total_time}"
return run

def func_1():
time.sleep(0.2) # 模拟函数运行所耗费的时间
print("我是func_1函数")
return "Hello func_1"

func_1 = get_runtime(func_1)
print(func_1())

# 结果
我是func_1函数
函数func_1运行时间: 0.2032308578491211

装饰器

下方代码与上方代码的运行结果一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import time

def get_runtime(func):

def run():
start = time.time() # 函数开始执行时间
func() # 运行传递进来的函数
end = time.time() # 函数结束执行时间
total_time = end - start # 函数运行所耗费的时间
return f"函数{func.__name__}运行时间: {total_time}"
return run

@get_runtime # 使用装饰器装饰该函数
def func_1():
time.sleep(0.2) # 模拟函数运行所耗费的时间
print("我是func_1函数")
return "Hello func_1"

print(func_1())

# 结果
我是func_1函数
函数func_1运行时间: 0.2032308578491211

带参数的被装饰函数

与带参数的闭包方法一样,只要接收传递*xx**xxx即可

模板

1
2
3
4
5
6
7
def 装饰器(func):
def 内部函数(*args,**kwargs):
# 调用前操作
result = func(*args,**kwargs)
# 调用后操作
return result
return 内部函数

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def wrapper(func):  # 装饰器只能接收一个参数,该参数只能为函数类型
"""装饰器"""

def run(*args,**kwargs):
"""输出执行日志"""
result = func(*args,**kwargs)
return "结果为: " + str(result) # 返回运算结果

return run # 返回内部函数

@wrapper
def add(num1, num2):
"""计算加法"""
result = num1 + num2
return result

print(add(1, 1)) # 执行函数
print(add(1, num2=1)) # 执行函数

# 结果
结果为: 2
结果为: 2

带参数的装饰器

如果装饰器函数本身也需要传递参数,则需要在装饰器外层再包裹一层函数,该函数用于接收参数供装饰器使用。这里的参数指的是装饰器的参数而不是被装饰函数的参数!

错误写法

装饰器只能接收一个参数,且该参数只能为函数类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def wrapper(func,flag):  # 装饰器只能接收一个参数,该参数只能为函数类型,这里写法错误
"""装饰器"""

def run(*args,**kwargs):
"""输出执行日志"""
if flag == "outlog": # 当flag为outlog时就输出运算日志
print("正在计算中...")
result = func(*args,**kwargs)
return "结果为: " + str(result) # 返回运算结果

return run # 返回内部函数


@wrapper("outlog") # 传递参数给装饰器
def add(num1,num2):
"""计算加法"""
result = num1 + num2
return result


print(add(1,1))

# 结果
TypeError: wrapper() missing 1 required positional argument: 'flag'

正确写法

需要在装饰器的外层再包裹一个函数,该函数用于接收参数、返回装饰器。要注意有参数的装饰器写法为@外层函数()而不是@外层函数,前者带括号表示立即运行该函数,而后者不带括号表示不运行该函数(就不会返回装饰器了)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
def pr_info(flag=""):
"""用于接收参数给装饰器wrapper使用"""

def wrapper(func): # 装饰器只能接收一个参数,该参数只能为函数类型
"""装饰器"""

def run(*args,**kwargs):
"""输出执行日志"""
if flag == "outlog": # 当flag为outlog时就输出运算日志
print("正在计算中...")
result = func(*args,**kwargs)
return "结果为: " + str(result) # 返回运算结果

return run # 返回内部函数

return wrapper # 返回装饰器


# @pr_info # 错误写法
@pr_info() # 不传递参数给装饰器
def add(num1, num2):
"""计算加法"""
result = num1 + num2
return result


@pr_info("outlog") # 传递参数给装饰器
def sub(num1, num2):
"""计算减法"""
result = num1 - num2
return result


print(add(1, 1)) # 不打印正在计算中...
print(sub(1, 1)) # 打印正在计算中...

# 结果
结果为: 2
正在计算中...
结果为: 0

接收任意数量的实参与任意数量的关键词实参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
def pr_info(flag="",*param, **kvs):
"""用于接收参数给装饰器wrapper使用"""

def wrapper(func): # 装饰器只能接收一个参数,该参数只能为函数类型
"""装饰器"""

def run(*args,**kwargs):
"""输出执行日志"""
# 打印传递进来的任意数量的实参与任意数量的关键字形参
print(param)
print(kvs)

# 输出日志
if flag == "outlog": # 当flag为outlog时就输出运算日志
print("正在计算中...")
result = func(*args,**kwargs)
return "结果为: " + str(result) # 返回运算结果

return run # 返回内部函数

return wrapper # 返回装饰器


@pr_info("outlog","a","b",color="red") # 传递参数给装饰器
def add(num1, num2):
"""计算加法"""
result = num1 + num2
return result

print(add(1, 1)) # 执行函数

# 结果
('a', 'b')
{'color': 'red'}
正在计算中...
结果为: 2

装饰类

装饰类

上方示例中都是使用函数来装饰函数,该处通过函数类装饰整个类。写完装饰器后只需要在需要被装饰的类上加@装饰器即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def wrapClass(cls):
"""定义一个装饰器"""
def run(*args,**kwargs):
print('类名:', cls.__name__)
return cls(*args,**kwargs)
return run


@wrapClass
class Test:
def __init__(self, num1, num2):
self.num1 = num1
self.num2 = num2

def fun(self):
"""进行加法运算"""
print(self.num1 + self.num2)


m = Test(1,1)
m.fun()

# 结果
类名: Test
2

装饰类方法

装饰类方法与装饰类差不多,直接在需要被装饰的方法上加@装饰器即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def WrapFunc(func):
"""定义一个装饰器"""
def run(*args,**kwargs):
print('函数名:', func.__name__)
return func(*args,**kwargs)
return run


class Test:
def __init__(self, num1, num2):
self.num1 = num1
self.num2 = num2

@WrapFunc
def add(self):
"""进行加法运算"""
print(self.num1 + self.num2)


def sub(self):
"""进行减法运算"""
print(self.num1 - self.num2)


m = Test(1,1)
m.add()
m.sub()

# 结果
函数名: add
2
0

多装饰器的执行顺序

当一个函数被多个装饰器装饰时,装饰顺序由下至上,调用顺序由上至下

下方使用两个装饰器装饰一个函数时,等同于如下闭包。函数会先被wrapper2给装饰后再被wrapper1装饰,调用时会先执行wrapper1的内部函数再执行后wrapper2的内部函数。

1
2
3
4
5
6
7
8
9
10
11
12
# 装饰器
@wrapper1
@wrapper2
def test():
xxx

# 闭包(方法1)
test = wrapper1(wrapper2(test))

# 闭包(方法2)
test = wrapper2(test)
test = wrapper1(test)

对于多装饰器的解决办法就是把装饰器改为非语法糖的闭包,如下所示(建议对下方代码进行断电调试,方便理解)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
def wrapper1(func):
"""装饰器1"""
print("我是wrapper1的外部函数")

def run_1():
print("我是wrapper1的内部函数")
func()
print("我是wrapper1的内部函数B")
return run_1

def wrapper2(func):
"""装饰器2"""
print("我是wrapper2的外部函数")

def run_2():
print("我是wrapper2的内部函数")
func()
print("我是wrapper2的内部函数B")
return run_2


def test():
"""被装饰的函数"""
print("我是被装饰的函数")


if __name__ == "__main__":
# test = wrapper1(wrapper2(test)) # 等同下方

test = wrapper2(test) # test = run_2(test)()
test = wrapper1(test) # test = run_1(run_2(test))() 递归封装
test()

"""
结果:
我是wrapper2的外部函数
我是wrapper1的外部函数
我是wrapper1的内部函数
我是wrapper2的内部函数
我是被装饰的函数
我是wrapper2的内部函数B
我是wrapper1的内部函数B

运行流程:
1. wrapper2先对test函数进行装饰,所以wrapper2的代码块就被执行了
test = wrapper2(test)
print("我是wrapper2的外部函数")
# 结果 --> 我是wrapper2的外部函数

2. wrapper2执行打印操作后就返回run_2函数,现在test就是run_2函数 (wrapper2执行结束)
return run_2

3. wrapper1对test进行装饰(也就是run_2函数),所以wrapper1的代码块就被执行了 (wrapper1执行结束)
test = wrapper1(test)
print("我是wrapper1的外部函数")
# 结果 --> 我是wrapper1的外部函数

4. wrapper1执行打印操作后就返回run_1函数,现在test就是run_1函数,run_1函数装饰run_2函数
return run_1

5. test()执行了run_1函数的代码块,也就是run_1函数的打印输出
test()
print("我是wrapper1的内部函数")
# 结果 --> 我是wrapper1的内部函数

6. run_1函数执行打印操作后,就执行run_1装饰的函数run_2
func()
print("我是wrapper2的内部函数")
# 结果 --> 我是wrapper2的内部函数

7. run_2函数执行打印操作后,就执行run_2装饰的函数test的代码块 (test执行结束)
func()
print("我是被装饰的函数")
# 结果 --> 我是被装饰的函数

8. test函数执行打印操作后,就执行run_2剩下的代码,也就是打印输出 (run_2执行结束)
print("我是wrapper2的内部函数B")
# 结果 --> 我是wrapper2的内部函数B

9. run_2函数执行打印操作后,就执行run_1剩下的代码,也就是打印输出 (run_1执行结束)
print("我是wrapper1的内部函数B")
# 结果 --> 我是wrapper1的内部函数B

10. run_1执行结束后程序结束
"""

类装饰器

定义类装饰器

介绍

类装饰器指的是使用类来定义装饰器,而不是用函数装饰器来装饰类。类装饰器一般使用内部的__call__()方法来定义

装饰函数

下方使用类定义了一个装饰器,该装饰器用于返回传入函数的名称

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Wrapper:
"""类装饰器"""
def __init__(self,func):
self.func = func # 需要被装饰的函数名


def __call__(self,*args,**kwargs):
print("函数名: %s" % self.func.__name__)
return self.func(*args,**kwargs)

@Wrapper
def test(num1,num2):
"""被类装饰器装饰的函数"""
return num1 + num2


print(test(1,1))

# 结果
函数名: test
2

装饰类

下方使用类定义了一个装饰器,该装饰器用于返回传入类的名称

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Wrapper:
"""类装饰器"""
def __init__(self,cls):
self.cls = cls # 需要被装饰的类名


def __call__(self,*args,**kwargs):
print("类名: %s" % self.cls.__name__)
return self.cls(*args,**kwargs)

@Wrapper
class Test:
"""被类装饰器装饰的类"""
def __init__(self,num1,num2):
self.num1 = num1
self.num2 = num2

def add(self):
"""加法运算"""
print(self.num1 + self.num2)


test = Test(1,1)
test.add()

# 结果
类名: Test
2

应用场景

统计运行次数

统计某个函数的运行次数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
def wrapper(func):
"""用于统计函数执行次数的装饰器"""

def run():
"""输出执行日志"""
run.count += 1
print("%s函数被执行: %s次" % (func.__name__,run.count))
func()
run.count = 0 # 统计次数
return run

@wrapper
def test():
"""模拟跑步"""
print("开始跑步")


test()
test()
test()
test()

# 结果
test函数被执行: 1
开始跑步
test函数被执行: 2
开始跑步
test函数被执行: 3
开始跑步
test函数被执行: 4
开始跑步

统计函数运行时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import time

def get_runtime(func):
# 在函数get_runtime内定义函数run,但函数run不被执行,而是当作函数get_runtime的返回值返回了
def run():
start = time.time() # 函数开始执行时间
func() # 运行传递进来的函数
end = time.time() # 函数结束执行时间
total_time = end - start # 函数运行所耗费的时间
return f"函数{func.__name__}运行时间: {total_time}"
return run

@get_runtime
def func_1():
time.sleep(0.2) # 模拟函数运行所耗费的时间
print("我是func_1函数")
return "Hello func_1" # 被装饰函数的返回值

print(func_1())

# 结果
我是func_1函数
函数func_1运行时间: 0.20908427238464355

写入日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import time

def write_log(func):
"""写入日志"""
def run():
start = time.time() # 函数开始执行时间
result = func() # 运行传递进来的函数
end = time.time() # 函数结束执行时间
total_time = end - start # 函数运行所耗费的时间

# 下列代码假装在写日志
log = {
'func': func.__name__,
'runtime': total_time,
'return': result
}
print("日志写入中.....")
print("写入完成.....")
print(log)

return log
return run

def func_1():
time.sleep(0.2) # 模拟函数运行所耗费的时间
print("我是func_1函数")
return "Hello func_1" # 被装饰函数的返回值

func_1 = write_log(func_1)
func_1()


# 结果
我是func_1函数
日志写入中.....
写入完成.....
{'func': 'func_1', 'runtime': 0.2035982608795166, 'return': 'Hello func_1'}



本文参考

https://www.runoob.com/w3cnote/python-func-decorators.html

https://blog.csdn.net/xiemanr/article/details/72510885

https://blog.csdn.net/weixin_33711647/article/details/92549215

https://mp.weixin.qq.com/s/7R7QsV5BflBk01-F8WMcBw