Python补遗(四)——装饰器
概述
装饰器本身就是就是一种特殊的函数,其主要作用为对已有的其他函数追加自定义的额外功能。比如说某函A数洋洋洒洒写了100行,现出于某种需求需要对其追加某种功能B,最简单的方法自然是直接修改函数代码本身,但考虑到该函数只在某些特殊情况下才需要执行额外功能B,因此直接修改代码会造成代码臃肿难以维护。也许你会说我可以用if-else分支对不同情况进行判断,那么如果该函数需要接入100种不同不同额外功能,难道需要在函数体内部写多达100个分支嘛?这显然是不合理的。因此,我们就考虑需要在不改变原函数代码的前提下,编写一段或多段额外的独立代码模块,对原函数进行装饰,从而起到根据不同情况追加功能的功能。
装饰器应该具有以下性质:
- 通用性:即装饰器应该作为独立的功能模块,普遍适用于一切函数的装饰,而与原函数保持无关性和独立性;
- 插件性:即装饰器可以作为一个插件,自由地根据需求对原函数进行装饰或不装饰;
本文将由浅及深,对装饰器进行详细说明:
代码示例
"""
@ 纯函数方式进行装饰
@ 装饰内容为统计原始函数地执行时间
@ deco函数是不带语法糖的装饰器实现
"""
import time
def deco(func):
startTime = time.time()
func()
endTime = time.time()
print("函数的执行用时为: %d ms"%((endTime-startTime)*1000))
def func():
sum = 0
for i in range(10000000):
sum += i**2
print(sum)
deco(func)
if False:
"""
@执行结果:
333333283333335000000
函数的执行用时为: 5667 ms
"""
"""
@ 带语法糖的装饰器实现
"""
import time
def deco(func):
def wrap():
startTime = time.time()
func()
endTime = time.time()
print("函数的执行用时为: %d ms"%((endTime-startTime)*1000))
return wrap
# 对原始函数进行deco装饰,@deco等同于 func = deco(func)
@deco
def func():
sum = 0
for i in range(10000000):
sum += i**2
print(sum)
func()
if False:
"""
@执行结果:
333333283333335000000
函数的执行用时为: 5667 ms
"""
"""
@ 当原始函数带有参数
"""
import time
def deco(func):
def wrap(a):
startTime = time.time()
func(a)
endTime = time.time()
print("函数的执行用时为: %d ms"%((endTime-startTime)*1000))
return wrap
@deco
def func(a):
sum = 0
for i in range(a):
sum += i**2
print(sum)
func(10000000)
if False:
"""
@执行结果:
333333283333335000000
函数的执行用时为: 5181 ms
"""
"""
@ 当原始函数传入不定长参数的时候
@ 这也是装饰器的通用写法
"""
def deco2(func):
def wrap(*args,**kwargs):
startTime = time.time()
func(*args,**kwargs)
endTime = time.time()
print("函数的执行用时为: %d ms"%((endTime-startTime)*1000))
return wrap
@deco2
def func2(*args):
sum = 0
for item in args:
for i in range(item):
sum += i**2
print(sum)
func2(100,1000,10000,100000,1000000)
if False:
"""
@执行结果:
333666495283346850
函数的执行用时为: 387 ms
"""
"""
@ 多个装饰器装饰同一个函数
"""
import time
def deco(func):
def wrap(*args,**kwargs):
print("程序开始计时...")
startTime = time.time()
func(*args,**kwargs)
endTime = time.time()
print("程序结束计时...")
print("函数的执行用时为: %d ms"%((endTime-startTime)*1000))
return wrap
def deco2(func):
def wrap(*args,**kwargs):
print("用户Samson开始操作...")
func(*args,**kwargs)
print("用户Samson操作完毕...")
return wrap
# 多个装饰器同时装饰原始函数的顺序是先由离函数近的装饰器进行装饰
# 等同于func = deco(deco2(func))
@deco
@deco2
def func2(*args):
sum = 0
for item in args:
for i in range(item):
sum += i**2
print("程序结果是:%d"%sum)
func2(100,1000,10000,100000,1000000)
if False:
"""
@执行结果:
程序开始计时...
用户Samson开始操作...
程序结果是:333666495283346850
用户Samson操作完毕...
程序结束计时...
函数的执行用时为: 434 ms
"""
"""
@装饰器函数本身带有参数
"""
import time
def deco(func):
def wrap(*args,**kwargs):
print("程序开始计时...")
startTime = time.time()
func(*args,**kwargs)
endTime = time.time()
print("程序结束计时...")
print("函数的执行用时为: %d ms"%((endTime-startTime)*1000))
return wrap
def get_deco2(user):
def deco2(func):
def wrap(*args,**kwargs):
print("用户%s开始操作..."%user)
func(*args,**kwargs)
print("用户%s操作完毕..."%user)
return wrap
return deco2
@deco
# get_deco2("Samson")意味着对get_deco2函数进行执行,执行的结果为返回一个deco2装饰器函数
# 因此需要注意一点:@xxx的装饰器语法本质上是在告诉Python解释器我是一个装饰器,请用它对原始函数进行装饰
# 本质上,其等于func2 = deco(get_deco2("Yixin")(fun2))
@get_deco2("Yixin")
def func2(*args):
sum = 0
for item in args:
for i in range(item):
sum += i**2
print("程序结果是:%d"%sum)
func2(100,1000,10000,100000,1000000)
if False:
"""
@执行结果:
程序开始计时...
用户Yixin开始操作...
程序结果是:333666495283346850
用户Yixin操作完毕...
程序结束计时...
函数的执行用时为: 370 ms
"""