装饰器

装饰器 decorator

在不改变原有函数代码,且保持原函数调用方法不变的情况下,给原函数增加新的功能(或者给类增加属性和方法)

装饰器定义与原型

  • 核心思想
    • 用一个函数(或者类)去装饰一个旧函数(或者类),造出一个新函数(或者新类)
  • 语法规则
    • 在原有的函数上加上@符号,装饰器会把下面的函数当做参数传递到装饰器中,@符又被称为语法糖
  • 应用场景
    • 引入日志,函数执行时间的统计,执行函数前的准备工作,执行函数后的处理工作,权限校验,缓存等场景中
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
# 装饰器的原型,利用闭包实现
def outer(f):
def inner():
print('这是inner函数的语句1')
f()
print('这是inner函数的语句2')

return inner


def demo():
print('这是demo函数的语句')


obj = outer(demo)
obj()
# 这是inner函数的语句1
# 这是demo函数的语句
# 这是inner函数的语句2

print()

# 改为装饰器的定义
# 此处等同于 demo2=outer(demo2)

@outer
def demo2():
print('这是使用装饰器的demo')

demo2()
# 这是inner函数的语句1
# 这是将要使用装饰器的demo
# 这是inner函数的语句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
import time


def cal_time(f):
def inner():
time1 = time.perf_counter()
f()
time2 = time.perf_counter()
elapsed = time2 - time1
print(f'函数运行了{elapsed}s')
return elapsed

return inner


@cal_time
def func():
for i in range(5):
time.sleep(0.1)
print('func运行结束')


func()
# func运行结束
# 函数运行了0.5152184049999999s

@wraps

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

def cal_time(f):
@wraps(f)
def inner():
time1 = time.perf_counter()
f()
time2 = time.perf_counter()
print(f'函数运行了{time2 - time1}s')

return inner

@cal_time
def func():
for i in range(5):
time.sleep(0.1)
print('func运行结束')

print(func)
print(func.__name__)

装饰器的嵌套

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 outer1(func):
def inner1():
print('装饰器1————1')
func()
print('装饰器1————2')

return inner1


def outer2(func):
def inner2():
print('装饰器2————3')
func()
print('装饰器2————4')

return inner2


@outer2
@outer1
def demo():
print('这是demo函数————5')


demo()
# 装饰器2————3
# 装饰器1————1
# 这是demo函数————5
# 装饰器1————2
# 装饰器2————4

装饰带有参数的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def outer(func):
def inner(name):
print('关于谁最美丽这件事')
func(name)

return inner


@outer
def love(name):
print(f'{name}女士最美丽')


love('duandaun')
# 关于谁最美丽这件事
# duandaun女士最美丽
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import time

def cal_time(f):
def inner(li):
t1 = time.perf_counter()
f(li)
t2 = time.perf_counter()
print(f'函数{f}执行了{t2-t1}s')
return inner

@cal_time
def sort_fun(li):
for i in range(len(li)-1):
for j in range(len(li)-1-i):
if li[j] > li[j+1]:
li[j],li[j+1] = li[j+1],li[j]
return li


li = [5,4,3,2,1]
sort_fun(li)
print(li)

装饰带有多参数的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def outer(func):
def inner(who, name, *args, **kwargs):
print(f'{name}回来啦')
func(who, name, *args, **kwargs)

return inner


@outer
def date(who, name, *args, **kwargs):
print(f'{who}今天和{name}出门约会啦')
print(f'我们吃了{args[0]}')
print(f'还一起看了电影{kwargs}')


date('huanghuang', 'duandaun', '烤肉', movie='笨小孩')
# duandaun回来啦
# huanghuang今天和duandaun出门约会啦
# 我们吃了烤肉
# 还一起看了电影{'movie': '笨小孩'}

带有参数的装饰器

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 extend_m(var):
def outer(func):
def inner1(name):
print(f'今天和{name}一起吃烤肉')
func(name)

def inner2(name):
print(f'今天和{name}一起吃火锅')
func(name)

if var == '烤肉':
return inner1
else:
return inner2

return outer


@extend_m('烤肉')
def date(name):
print(f'和{name}约会啦,很开心!')


date('duanduan')
# 今天和duanduan一起吃烤肉
# 和duanduan约会啦,很开心!

类装饰器修饰函数

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
# 类装饰器装饰函数
class Outer():

# 魔术方法:当把该类的对象当做函数调用时,自动触发obj()
def __call__(self, func):
self.func = func
return self.inner

def inner(self, who):
print(f'和{who}一起看电影啦')
self.func(who)
print('约会吃烤肉啦哇')


# 注意这里应该是Outer()
# Outer() ===> obj, @obj ===>obj(date) , call (date)===> inner()
@Outer()
def date(who):
print(f'今天和{who}约会啦')


date('duanduan')
# 和duanduan一起看电影啦
# 今天和duanduan约会啦
# 约会吃烤肉啦哇

class Outer:
def __init__(self,func):
self.func = func

def __call__(self,*args, **kwargs):
print("今天出门啦")
result = self.func(*args, **kwargs)
print("今天结束啦")
return result

@Outer
def date(who):
print(f"今天和{who}出门约会啦")
print("出门看电影、吃烤肉啦")
return success

print(date("duanduan"))

带有参数的类装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Outer:
def __init__(self, name):
self.name = name

def __call__(self,func):
def inner(*args, **kwargs):
print("今天出门啦")
print(f"和{self.name}出门约会啦")
result = func(*args, **kwargs)
print("今天结束啦")
return result
return inner

@Outer("duanduan")
def date(place:str):
print(f"今天去{place}")
print("出门看电影、吃烤肉啦")
return success

print(date("北京"))

用类方法装饰函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Outer():
def newinner(func):
Outer.func = func # 把传递进来的函数定义为类方法
return Outer.inner # 同时返回一个新的类方法

def inner(who):
print(f'和{who}一起看电影啦')
Outer.func(who)
print(f'约会吃烤肉啦哇')


@Outer.newinner # Outer.newinner(date) ===> Outer.inner
def date(who):
print(f'今天和{who}约会啦')


date('duanduan')

使用函数装饰器装饰类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def extend_c(cls):
def func2():
print('在装饰器中扩展的新方法func2')

cls.func2 = func2 # 将刚才定义的方法赋值给类
cls.name = '在装饰器中追加的新属性'

# 返回时,把追加类新成员的类返回
return cls


@extend_c # extend_c(Demo) ===> cls ===> Demo
class Demo():
# 定义一个类方法
def func():
print('Demo类中的func方法')


Demo.func() # Demo类中的func方法
Demo.func2() # 在装饰器中扩展的新方法func2
print(Demo.name) # 在装饰器中追加的新属性

使用类装饰器装饰类

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
class Extend_c():
def __call__(self, cls):
self.cls = cls
return self.newfunc

def newfunc(self):
self.cls.name = '在类装饰器中追加的新属性'
self.cls.func2 = self.func2
# 返回
return self.cls()

def func2(self):
print('在类装饰器中追加的新方法 func2')


@Extend_c() # Extend_c() ===> obj ===> @obj(Demo) ===> __call__(Demo) ===> newfunc()
class Demo():
# 定义一个普通的方法
def func(self):
print('Demo类中的一个普通的方法')


obj = Demo()
obj.func() # Demo类中的一个普通的方法
obj.func2() # 在类装饰器中追加的新方法 func2
print(obj.name) # 在类装饰器中追加的新属性
print(obj) # <__main__.Demo object at 0x10e77ddd0>

更新: 2024-05-16 12:24:07
原文: https://www.yuque.com/zacharyblock/cx2om6/fas3gy4akb8thh3c