鱼C论坛

 找回密码
 立即注册
查看: 1767|回复: 0

[技术交流] 实用干货:如何理解装饰器(3)

[复制链接]
发表于 2017-3-30 20:25:44 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
装饰器揭秘

前面的例子,我们可以使用装饰器的语法:

  1. @my_shiny_new_decorator
  2. def another_stand_alone_function() :
  3.     print "Leave me alone"

  4. another_stand_alone_function()
  5. #输出 :
  6. #Before the function runs
  7. #Leave me alone
  8. #After the function runs
  9. 当然你也可以累积装饰:

  10. def bread(func) :
  11.     def wrapper() :
  12.         print "</''''''\>"
  13.         func()
  14.         print "<\______/>"
  15.     return wrapper

  16. def ingredients(func) :
  17.     def wrapper() :
  18.         print "#tomatoes#"
  19.         func()
  20.         print "~salad~"
  21.     return wrapper

  22. def sandwich(food="--ham--") :
  23.     print food

  24. sandwich()
  25. #输出 : --ham--
  26. sandwich = bread(ingredients(sandwich))
  27. sandwich()
  28. #outputs :
  29. #</''''''\>
  30. # #tomatoes#
  31. # --ham--
  32. # ~salad~
  33. #<\______/>
复制代码


使用python装饰器语法:

  1. @bread
  2. @ingredients
  3. def sandwich(food="--ham--") :
  4.     print food

  5. sandwich()
  6. #输出 :
  7. #</''''''\>
  8. # #tomatoes#
  9. # --ham--
  10. # ~salad~
  11. #<\______/>
复制代码


装饰器的顺序很重要,需要注意:

  1. @ingredients
  2. @bread
  3. def strange_sandwich(food="--ham--") :
  4.     print food

  5. strange_sandwich()
  6. #输出 :
  7. ##tomatoes#
  8. #</''''''\>
  9. # --ham--
  10. #<\______/>
  11. # ~salad~
  12. 最后回答前面提到的问题:

  13. # 装饰器makebold用于转换为粗体
  14. def makebold(fn):
  15.     # 结果返回该函数
  16.     def wrapper():
  17.         # 插入一些执行前后的代码
  18.         return "<b>" + fn() + "</b>"
  19.     return wrapper

  20. # 装饰器makeitalic用于转换为斜体
  21. def makeitalic(fn):
  22.     # 结果返回该函数
  23.     def wrapper():
  24.         # 插入一些执行前后的代码
  25.         return "<i>" + fn() + "</i>"
  26.     return wrapper

  27. @makebold
  28. @makeitalic
  29. def say():
  30.     return "hello"

  31. print say()
  32. #输出: <b><i>hello</i></b>

  33. # 等同于
  34. def say():
  35.     return "hello"
  36. say = makebold(makeitalic(say))

  37. print say()
  38. #输出: <b><i>hello</i></b>
复制代码


内置的装饰器

内置的装饰器有三个,分别是staticmethod、classmethod和property,作用分别是把类中定义的实例方法变成静态方法、类方法和类属性。由于模块里可以定义函数,所以静态方法和类方法的用处并不是太多,除非你想要完全的面向对象编程。而属性也不是不可或缺的,Java没有属性也一样活得很滋润。从我个人的Python经验来看,我没有使用过property,使用staticmethod和classmethod的频率也非常低。

  1. class Rabbit(object):
  2.      
  3.     def __init__(self, name):
  4.         self._name = name
  5.      
  6.     @staticmethod
  7.     def newRabbit(name):
  8.         return Rabbit(name)
  9.      
  10.     @classmethod
  11.     def newRabbit2(cls):
  12.         return Rabbit('')
  13.      
  14.     @property
  15.     def name(self):
  16.         return self._name
复制代码


这里定义的属性是一个只读属性,如果需要可写,则需要再定义一个setter:

  1. @name.setter
  2. def name(self, name):
  3.     self._name = name
复制代码


functools模块

functools模块提供了两个装饰器。这个模块是Python 2.5后新增的,一般来说大家用的应该都高于这个版本。

2.3.1. wraps(wrapped[, assigned][, updated]):
这是一个很有用的装饰器。看过前一篇反射的朋友应该知道,函数是有几个特殊属性比如函数名,在被装饰后,上例中的函数名foo会变成包装函数的名字wrapper,如果你希望使用反射,可能会导致意外的结果。这个装饰器可以解决这个问题,它能将装饰过的函数的特殊属性保留。


  1. import time
  2. import functools

  3. def timeit(func):
  4.     @functools.wraps(func)
  5.     def wrapper():
  6.         start = time.clock()
  7.         func()
  8.         end =time.clock()
  9.         print 'used:', end - start
  10.     return wrapper

  11. @timeit
  12. def foo():
  13.     print 'in foo()'

  14. foo()
  15. print foo.__name__
复制代码


首先注意第5行,如果注释这一行,foo.__name__将是'wrapper'。另外相信你也注意到了,这个装饰器竟然带有一个参数。实际上,他还有另外两个可选的参数,assigned中的属性名将使用赋值的方式替换,而updated中的属性名将使用update的方式合并,你可以通过查看functools的源代码获得它们的默认值。对于这个装饰器,相当于wrapper = functools.wraps(func)(wrapper)。

2.3.2. total_ordering(cls):
这个装饰器在特定的场合有一定用处,但是它是在Python 2.7后新增的。它的作用是为实现了至少__lt__、__le__、__gt__、__ge__其中一个的类加上其他的比较方法,这是一个类装饰器。如果觉得不好理解,不妨仔细看看这个装饰器的源代码:

  1. def total_ordering(cls):
  2. 54      """Class decorator that fills in missing ordering methods"""
  3. 55      convert = {
  4. 56          '__lt__': [('__gt__', lambda self, other: other < self),
  5. 57                     ('__le__', lambda self, other: not other < self),
  6. 58                     ('__ge__', lambda self, other: not self < other)],
  7. 59          '__le__': [('__ge__', lambda self, other: other <= self),
  8. 60                     ('__lt__', lambda self, other: not other <= self),
  9. 61                     ('__gt__', lambda self, other: not self <= other)],
  10. 62          '__gt__': [('__lt__', lambda self, other: other > self),
  11. 63                     ('__ge__', lambda self, other: not other > self),
  12. 64                     ('__le__', lambda self, other: not self > other)],
  13. 65          '__ge__': [('__le__', lambda self, other: other >= self),
  14. 66                     ('__gt__', lambda self, other: not other >= self),
  15. 67                     ('__lt__', lambda self, other: not self >= other)]
  16. 68      }
  17. 69      roots = set(dir(cls)) & set(convert)
  18. 70      if not roots:
  19. 71          raise ValueError('must define at least one ordering operation: < > <= >=')
  20. 72      root = max(roots)       # prefer __lt__ to __le__ to __gt__ to __ge__
  21. 73      for opname, opfunc in convert[root]:
  22. 74          if opname not in roots:
  23. 75              opfunc.__name__ = opname
  24. 76              opfunc.__doc__ = getattr(int, opname).__doc__
  25. 77              setattr(cls, opname, opfunc)
  26. 78      return cls
复制代码

本帖被以下淘专辑推荐:

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-6-13 23:53

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表