作用
在不改变原有函数代码,且保持原函数调用方法不变的情况下,给原函数增加新的功能(或者给类增加属性和方法)
- 核心思想:用一个函数(或者类)去装饰另一个函数(或者类),造出一个新函数(或者新类)
- 应用场景:引入日志,函数执行时间的统计,执行函数前的准备工作,执行函数后的处理工作,权限校验,缓存等
- 语法规则:在原有的函数上加上 @符,装饰器会把下面的函数当作参数传递到装饰器中,@符又被成为 语法糖
装饰器原型
# 利用闭包函数把函数当作参数传递,并且在函数内去调用传递进来的函数,并返回一个函数
def outer(f):
def inner():
print('我在函数func之前被执行')
f()
print('我在函数func之后被执行')
return inner
@outer
def func():
print('我是函数func')
func()
输出:
装饰器装饰函数
无参数函数
import time
def runtime(f):
def inner():
start = time.perf_counter()
f()
interval = time.perf_counter() - start
print(f'函数的调用执行时间为:{interval}')
return inner
@runtime
def func():
for i in range(5):
print(i, end=" ")
time.sleep(1)
func()
输出:
分析:
这里用装饰器runtime
装饰了函数func
,runtime
返回inner
,当我们调用func()
时就相当于调用了inner()
;此时首先记录当前时间,然后调用func()
,最后计算时间差并输出
有参数函数
import time
def runtime(f):
def inner(n):
start = time.perf_counter()
f(n)
interval = time.perf_counter() - start
print(f'函数的调用执行时间为:{interval}')
return inner
@runtime
def func(n):
for i in range(n):
print(i, end=" ")
time.sleep(1)
func(9)
输出:
装饰器嵌套
def outer(f):
def inner():
print('第二个被执行的部分') # print2
f()
print('第三个被执行的部分') # print3
return inner
def extra(f):
def extra_inner():
print('第一个被执行的部分') # print1
f()
print('最后被执行的部分') # print4
return extra_inner
@extra
@outer
def func():
print('函数主体')
func()
输出:
分析:
先使用离得近的outer
装饰器装饰func
函数,返回了一个inner
函数,再使用上面的extra
装饰器,装饰返回的inner
函数,并返回extra_inner
函数。执行过程:
带有参数的装饰器
如果你的装饰器需要参数,那么就给当前的装饰器套一个壳,用于接收装饰器的参数
def extra(var):
def outer(func):
def inner1():
print('功能1')
func()
def inner2():
print('功能2')
func()
if var == 1:
return inner1
else:
return inner2
return outer
@extra(2)
def func():
print('函数主体')
func()
输出:
用类装饰器装饰函数
import time
class RunTime:
# 魔术方法:当把该类的对象当作函数调用时自动触发
def __call__(self, func):
self.func = func # 把传进来的函数作为对象的成员方法
return self.inner # 返回一个函数
# 在定义的需要返回的新方法中 去进行装饰和处理
def inner(self, var):
start = time.time()
self.func(var)
print('函数运行时长:', time.time() - start)
@RunTime()
def func(nums):
# 插入排序
for i in range(len(nums)):
for j in range(i):
if nums[i] < nums[j]:
nums = nums[:j] + [nums[i]] + nums[j:i] + nums[i + 1:]
print(nums)
import random
nums = list(range(20))
random.shuffle(nums)
print(nums)
func(nums)
print(func)
输出:
分析:
用类做装饰器时相当于把类当作函数调用,这会触发__call__
魔术方法,返回inner
函数;最后一行的输出说明func
确实是RunTime
类的函数
用类方法装饰函数
import time
class RunTime():
def outer(func):
RunTime.func = func # 把传递进来的函数定义为类方法
return RunTime.inner # 同时返回一个新的类方法
def inner(nums):
start = time.time()
RunTime.func(nums)
print('函数运行时长:', time.time() - start)
@RunTime.outer
def func(nums):
# 插入排序
for i in range(len(nums)):
for j in range(i):
if nums[i] < nums[j]:
nums = nums[:j] + [nums[i]] + nums[j:i] + nums[i + 1:]
print(nums)
import random
nums = list(range(20))
random.shuffle(nums)
print(nums)
func(nums)
print(func)
输出结果和上例一样,原理和普通函数装饰器大体相同
装饰器装饰类
用装饰器装饰类,目的是在不改变类的定义和调用的情况下给类增加新的成员(属性或方法)
用函数装饰类
def extra(cls):
def func2():
print('我是在装饰器中追加的新方法')
cls.func2 = func2 # 把刚才定义的方法赋值给类
cls.attr = '我是在装饰器中追加的新属性'
#返回时,把追加了新成员和新方法的类返回
return cls
@extra
class Demo():
def func():
print('我是Demo类中定义的方法')
Demo.func() # 此时的Demo类是通过装饰器更新过的Demo类
Demo.func2()
print(Demo.attr)
输出:
用类装饰类
class Extra():
def __call__(self, cls):
# 把接收的类赋值给当前对象作为一个属性
self.cls = cls
# 返回一个函数
return self.newfunc
def newfunc(self):
self.cls.attr = '我是在类装饰器中追加的新属性'
self.cls.func2 = self.func2
# 返回传递进来的类的实例化结果
return self.cls()
def func2(self):
print('我是在类装饰器中追加的新方法')
@Extra()
class Demo():
def func(self):
print('我是Demo类中定义的方法')
obj = Demo()
obj.func()
obj.func2()
print(obj.attr)
print(obj)
输出:
分析:
用类做装饰器时相当于把类当作函数调用,这会触发__call__
魔术方法,返回self.newfunc
函数,因而实例化Demo
类相当于调用了self.newfunc
函数,该函数 给Demo
类增加了一个新方法和一个新属性,并将Demo
类的实例化结果返回