Python 装饰器 的产生 演进 让你彻底明白装饰器原理

客官°小女子只卖身不卖艺 2024-04-18 16:17 132阅读 0赞

想要给一个已经写好的函数“加点料”,怎么办?

装饰器的产生

最初大约在2000年左右,程序员是这样做的:

  1. #定义好的原始函数
  2. def say():
  3. print( "hello world!")
  4. #很久很久以前的做法
  5. def debug(func):#接收函数地址
  6. def wrapper():#替换原函数的新函数地址
  7. print ("[DEBUG]: enter {}()".format(func.__name__))
  8. return func()
  9. return wrapper
  10. say = debug(say)#这就是装饰器的原始模样吧
  11. say()#其实是wrapper()
  12. 输出:
  13. [DEBUG]: enter say()#加的料
  14. hello world!

其实就是一个加料的新函数wrapper替换原函数

装饰器的演进1:只是加入@语法糖

  1. def debug(func):#加了@语法糖!
  2. def wrapper():
  3. print ("@语法糖 [DEBUG]: enter {}()".format(func.__name__))
  4. return func()
  5. return wrapper
  6. @debug
  7. def say():
  8. print ("hello world!")
  9. say()#其实是wrapper()
  10. 输入:
  11. @语法糖 [DEBUG]: enter say()
  12. hello world!
  13. @debug
  14. def say():
  15. print ("hello world!")
  16. //等价于:
  17. def say():
  18. print ("hello world!")
  19. say = debug(say)

装饰器的演进2:函数加入固定参数

  1. def debug(func):#把函数接过来返回包装后的函数!wrapper才是替换的函数
  2. def wrapper(n): # 指定一致的参数
  3. print( "[DEBUG]: enter {}()".format(func.__name__))
  4. return func(n)
  5. return wrapper # 返回包装过的新函数
  6. @debug #可以理解为:用debug中的wapper替换下面的函数
  7. def say(something):
  8. print (something)
  9. say('hello world!')#其实是wrapper('hello world!')
  10. 输出:
  11. [DEBUG]: enter say()
  12. hello world!

装饰器的演进3:函数加入任意参数

  1. def debug(func):#接收被替换函数
  2. def wrapper(*args, **kwargs): # 新函数指定任意参数
  3. print ("[DEBUG]: enter {}()".format(func.__name__))
  4. print('*args:',end=' ')
  5. print(*args)
  6. print('kwargs:',end=' ')
  7. print(kwargs)
  8. print('------------------------------------------')
  9. return func(*args, **kwargs)
  10. return wrapper
  11. @debug
  12. def say(*args, **kwargs):
  13. print(*args,end=' ')
  14. print(kwargs)
  15. say(1,2,3,'hello world!',key1 = 1,key2 = 2)#实际上是wrapper(1,2,3..)/或者是:debug(say)(1,2,3..)
  16. 输出:
  17. [DEBUG]: enter say()
  18. *args: 1 2 3 hello world!
  19. kwargs: { 'key1': 1, 'key2': 2}
  20. ------------------------------------------
  21. 1 2 3 hello world! { 'key1': 1, 'key2': 2}

装饰器的演进4:装饰器加入参数

  1. def logging(level):#最外层用于接受第一个参数
  2. def wrapper(func):
  3. def inner_wrapper(*args, **kwargs):
  4. print ("[{}]: enter function {}()".format(level,func.__name__))
  5. return func(*args, **kwargs)
  6. return inner_wrapper
  7. return wrapper
  8. @logging(level='INFO')
  9. def say(something):
  10. print( "say {}!".format(something))
  11. @logging(level='DEBUG')
  12. def do(something):
  13. print ("do {}!".format(something))
  14. say('hello')
  15. do("my work")
  16. # 如果没有使用@语法,say('hello') == logging(level='INFO')(say)('hello')

装饰器的演进5:类装饰器

**
装饰器函数其实是这样一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。
在Python中一般callable对象都是函数,但也有例外。
只要某个对象重载了__call__()方法,那么这个对象就是callable的。
**

  1. class Test():
  2. def __call__(self):
  3. print ('call me!')
  4. t = Test()
  5. t() # call me 想像不像函数

回到装饰器上的概念上来,装饰器要求接受一个callable对象,
并返回一个callable对象(不太严谨,详见后文)。那么用类来实现也是也可以的。
我们可以让类的构造函数__init__()接受一个函数,
然后重载__call__()并返回一个函数,也可以达到装饰器函数的效果

  1. class logging(object):
  2. def __init__(self, func):
  3. self.func = func
  4. def __call__(self, *args, **kwargs):
  5. print ("[DEBUG]: enter function {func}()".format(
  6. func=self.func.__name__))
  7. return self.func(*args, **kwargs)
  8. @logging
  9. def say(something):
  10. print ("say {}!".format(something))
  11. say('hello world!')
  12. 输出:
  13. [DEBUG]: enter function say()
  14. say hello world!!

‘’’
如果需要通过类形式实现带参数的装饰器,那么会比前面的例子稍微复杂一点。
那么在构造函数里接受的就不是一个函数,而是传入的参数。通过类把这些参数保存起来。
然后在重载__call__方法是就需要接受一个函数并返回一个函数。’’’

  1. class logging(object):
  2. def __init__(self, level='INFO'):
  3. self.level = level
  4. def __call__(self, func): # 接受函数
  5. def wrapper(*args, **kwargs):
  6. print( "[{level}]: enter function {func}()".format(
  7. level=self.level,
  8. func=func.__name__))
  9. func(*args, **kwargs)
  10. return wrapper #返回函数
  11. @logging(level='INFO')
  12. def say(something):
  13. print( "say {}!".format(something))
  14. say('hello world!')
  15. 输出:
  16. [INFO]: enter function say()
  17. say hello world!!

发表评论

表情:
评论列表 (有 0 条评论,132人围观)

还没有评论,来说两句吧...

相关阅读