Published in:2024-10-24 |

装饰器

一、概念

1
2
3
4
5
6
7
8
9
1. 装饰器(Decoration):
- 装饰器是一种设计模式,经常用来实现"面向切面的编程"(AOP: 实现在不修改源代码的情况下,给程序动态添加功能的一种技术)

2. 装饰器的作用:
- 装饰器允许向一个现有的对象(函数)添加新的功能,同时又不改变其结构
- 可以抽离出大量的函数中的和业务无关的功能

3. 应用场景:
- 插入日志、性能测试、事务处理、缓存、中间件、权限控制等

举个栗子:现在需要计算某个函数的执行时间

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
import time


def fun1():
start = time.time()
s = 0
for i in range(1, 100001):
s += i
print(f'和为:{s}')
t = time.time() - start
print(f'函数的执行时间为:{t:.10f}')


fun1()


def fun2():
start = time.time()
s = 1
for i in range(1, 100001):
s *= i
print(f'乘积为:{s}')
t = time.time() - start
print(f'函数的执行时间为:{t:.10f}')


fun2()

如果要把计算时间的代码抽离出来,此时就可以使用装饰器来实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import time
def get_time(func):
def wrapper():
start = time.time()
func()
t = time.time() - start
print(f'函数的执行时间为:{t}')
return wrapper

@get_time
def fun2():
s = 1
for i in range(1, 100001):
s *= i
print(f'乘积为:{s}')

fun2()

二、装饰器详解

1、装饰器
1
2
3
4
装饰器:
1. 关键字:@,在被修饰的函数的前一行加入
2. 本质:装饰器的本质就是一个函数
3. 原理:在调用被装饰的函数时,被装饰的函数体的代码并不会被直接执行。而是在调用被装饰的函数时,将该函数传递给装饰器
2、装饰器的基本形式

并不是真正的装饰器(有问题)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 装饰器函数必须要有一个参数,来接收被装饰的函数(名)
def my_decoration(func):
#print('这里写要装饰的东西,要给被装饰的函数添加的功能')
print('*'*10,'我是华丽丽的分隔线','*'*10)
return func


@my_decoration
def f():
print('这是一个函数')


# 这里调用被装饰的函数时,实际上先调用了装饰器,将函数本身
# 传递给装饰器函数,然后执行装饰器函数内部的代码
f()


@my_decoration
def f2():
print('这是另一个函数')

f2()
3、装饰器-内嵌函数

并不是真正的装饰器

1
2
3
4
5
6
7
8
9
10
11
12
def my_decoration(func):
def wrapper(): # 这里和上述的基本形式本质上是一样
print('这是要装饰的内容')
wrapper()
return func


@my_decoration
def f2():
print('这是另一个函数')

f2()
4、装饰器-闭包函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def my_decoration(func):
def wrapper():
print('\n'+'*'*10,'start','*'*10)
func() # 这是调用被装饰的函数
print('*'*11,'end','*'*11,'\n')
return wrapper


@my_decoration
def f():
print('这是另一个函数')


f()
5、装饰器闭包原理剖析
1
2
3
4
5
6
7
8
# 闭包:内函数引用了外函数的局部变量,并且外函数返回了内函数对象本身
def outer(x):
def inner():
return x
return inner

ot = outer('哈哈哈')
print(ot())
1
2
3
4
5
6
7
8
9
10
11
12
13
# 在上述的代码中,给外函数传递的是一个字符串类型的参数,其实也可给外函数传递一个函数对象
def outer(x):
def inner():
x() # 这里实际是调用了f1()函数
return inner


def f1():
print('这是f1函数')


ot = outer(f1)
ot()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# @语法糖
def outer(func):
def inner():
print(1111111111)
func() # 这里实际是调用了f1()函数
print(22222222)
return inner


# def f1():
# print('这是f1函数')
# ot = outer(f1) # 1. 这是闭包的调用语法
# ot()

@outer # 2. 这是Python的装饰器的语法糖
def f2():
print('这是f2函数')

f2()

三、带参数的装饰器

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
def my_decoration(a):
def wrapper(func):
def inner():
if a < 10:
print(1111)
else:
print(2222)
func()
return inner
return wrapper


@my_decoration(a=5)
def f1():
print('这是第一个函数')


f1()

@my_decoration(a=20)
def f2():
print('这是第二个函数')


f2()

四、类装饰器

1
装饰器不一定只能用函数来实现,也可以使用类来装饰,用法与函数装饰器区别不大,实质上是调用了类方法中__call__魔法方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class logging:
def __init__(self, func):
print('__init__',func)
self.__func = func

def __call__(self):
print(1111111)
return self.__func()


@logging
def hello():
print('hello 你好呀')


hello() # 调用了类装饰器中魔法方法 __call__ 敲敲就会了

五、内置装饰器

Python语言本身也有一些装饰器,比如@property

1
2
3
4
5
6
7
class Person:
def __init__(self,name,age):
self.name = name
self.age = age

gou = Person('二狗',18)
gou.age = 20 # 1. 属性暴露 2. 可以外界随意更改

修改:

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

def set_age(self, age):
if isinstance(age, int):
if 0 < age < 100:
self.__age = age
else:
raise ValueError('年龄超出范围')
else:
raise TypeError('年龄类型错误')


def get_age(self):
return self.__age


gou = Person('二狗', 18)
gou.set_age('abc') # TypeError: 年龄类型错误
print(gou.get_age())

再次修改:

1
2
3
4
5
6
7
8
class Person:
同上....
age = property(fget=get_age, fset=set_age)


gou = Person('二狗', 18)
gou.age = 200 # ValueError: 年龄超出范围
print(gou.age)
@property
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 使类中的方法可以像属性一样调用
class Person:
def __init__(self, name, age):
self.__name = name
self.__age = age

@property
def age(self):
return self.__age

@age.setter
def age(self,a):
self.__age = a


gou = Person('二狗', 18)
gou.age = 20 # 调用 setter
print(gou.age) # 调用 property

@staticmethod

将类中的方法设置为静态方法,它不需要创建实例对象,就可以使用类名来调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Person:
x = 100
xxxxxx...... 其它代码自己补

@staticmethod
def f():
print(Person.x) # 有一个方法不需要去访问实例属性
print('静态方法')


Person.f() # 可以直接用类名调用

p = Person('Tom',18)
p.f() # 也可以用对象调用
@classmethod
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Person:
xxxxxxxxxxxxx..............
@staticmethod
def f():
print(Person.x)
print('静态方法')

@classmethod
def n(cls): # 不用写类名 这里的cls就是类名
print(cls,type(cls))
print(isinstance(cls,Person))
print(cls.x)

Person.n()

p = Person('Tom',18)
p.n()
Prev:
路由器与交换机学习
Next: