Python 装饰器 的产生 演进 让你彻底明白装饰器原理
想要给一个已经写好的函数“加点料”,怎么办?
装饰器的产生
最初大约在2000年左右,程序员是这样做的:
#定义好的原始函数
def say():
print( "hello world!")
#很久很久以前的做法
def debug(func):#接收函数地址
def wrapper():#替换原函数的新函数地址
print ("[DEBUG]: enter {}()".format(func.__name__))
return func()
return wrapper
say = debug(say)#这就是装饰器的原始模样吧
say()#其实是wrapper()
输出:
[DEBUG]: enter say()#加的料
hello world!
其实就是一个加料的新函数wrapper替换原函数
装饰器的演进1:只是加入@语法糖
def debug(func):#加了@语法糖!
def wrapper():
print ("@语法糖 [DEBUG]: enter {}()".format(func.__name__))
return func()
return wrapper
@debug
def say():
print ("hello world!")
say()#其实是wrapper()
输入:
@语法糖 [DEBUG]: enter say()
hello world!
@debug
def say():
print ("hello world!")
//等价于:
def say():
print ("hello world!")
say = debug(say)
装饰器的演进2:函数加入固定参数
def debug(func):#把函数接过来返回包装后的函数!wrapper才是替换的函数
def wrapper(n): # 指定一致的参数
print( "[DEBUG]: enter {}()".format(func.__name__))
return func(n)
return wrapper # 返回包装过的新函数
@debug #可以理解为:用debug中的wapper替换下面的函数
def say(something):
print (something)
say('hello world!')#其实是wrapper('hello world!')
输出:
[DEBUG]: enter say()
hello world!
装饰器的演进3:函数加入任意参数
def debug(func):#接收被替换函数
def wrapper(*args, **kwargs): # 新函数指定任意参数
print ("[DEBUG]: enter {}()".format(func.__name__))
print('*args:',end=' ')
print(*args)
print('kwargs:',end=' ')
print(kwargs)
print('------------------------------------------')
return func(*args, **kwargs)
return wrapper
@debug
def say(*args, **kwargs):
print(*args,end=' ')
print(kwargs)
say(1,2,3,'hello world!',key1 = 1,key2 = 2)#实际上是wrapper(1,2,3..)/或者是:debug(say)(1,2,3..)
输出:
[DEBUG]: enter say()
*args: 1 2 3 hello world!
kwargs: { 'key1': 1, 'key2': 2}
------------------------------------------
1 2 3 hello world! { 'key1': 1, 'key2': 2}
装饰器的演进4:装饰器加入参数
def logging(level):#最外层用于接受第一个参数
def wrapper(func):
def inner_wrapper(*args, **kwargs):
print ("[{}]: enter function {}()".format(level,func.__name__))
return func(*args, **kwargs)
return inner_wrapper
return wrapper
@logging(level='INFO')
def say(something):
print( "say {}!".format(something))
@logging(level='DEBUG')
def do(something):
print ("do {}!".format(something))
say('hello')
do("my work")
# 如果没有使用@语法,say('hello') == logging(level='INFO')(say)('hello')
装饰器的演进5:类装饰器
**
装饰器函数其实是这样一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。
在Python中一般callable对象都是函数,但也有例外。
只要某个对象重载了__call__()方法,那么这个对象就是callable的。
**
class Test():
def __call__(self):
print ('call me!')
t = Test()
t() # call me 想像不像函数
回到装饰器上的概念上来,装饰器要求接受一个callable对象,
并返回一个callable对象(不太严谨,详见后文)。那么用类来实现也是也可以的。
我们可以让类的构造函数__init__()接受一个函数,
然后重载__call__()并返回一个函数,也可以达到装饰器函数的效果
class logging(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print ("[DEBUG]: enter function {func}()".format(
func=self.func.__name__))
return self.func(*args, **kwargs)
@logging
def say(something):
print ("say {}!".format(something))
say('hello world!')
输出:
[DEBUG]: enter function say()
say hello world!!
‘’’
如果需要通过类形式实现带参数的装饰器,那么会比前面的例子稍微复杂一点。
那么在构造函数里接受的就不是一个函数,而是传入的参数。通过类把这些参数保存起来。
然后在重载__call__方法是就需要接受一个函数并返回一个函数。’’’
class logging(object):
def __init__(self, level='INFO'):
self.level = level
def __call__(self, func): # 接受函数
def wrapper(*args, **kwargs):
print( "[{level}]: enter function {func}()".format(
level=self.level,
func=func.__name__))
func(*args, **kwargs)
return wrapper #返回函数
@logging(level='INFO')
def say(something):
print( "say {}!".format(something))
say('hello world!')
输出:
[INFO]: enter function say()
say hello world!!
还没有评论,来说两句吧...