Python 装饰器之 functools.wraps,,这次需要讲的更细一些
Python 装饰器之 functools.wraps,,这次需要讲的更细一些
Decorator
在以前的 Blog 中曾经简单写过 Decorator。这次需要讲的更细一些。
Decorator 通过返回包装对象实现间接调用,以此插入额外逻辑。是从老大那边偷来的哪里摘抄来的,应该算是言简意赅了。
@dec2 @dec1 def func(arg1, arg2, ...): pass
可以还原成
def func(arg1, arg2, ...): pass func = dec2(dec1(func))
@decomaker(argA, argB, ...) def func(arg1, arg2, ...): pass
可以还原成
func = decomaker(argA, argB, ...)(func)
In [1]: def outer(func): ...: def inner(): ...: print "before func" ...: ret = func() ...: return ret + 1 ...: return inner #返回 inner 函数对象 ...: In [2]: @outer # 解释器执⾏行 foo = outer(foo) ...: def foo(): ...: return 1 ...: In [3]: foo Out[3]: <function __main__.inner> In [4]: foo() before func Out[4]: 2
这个过程中执行了下面几步
- 函数 foo 作为 装饰器 outer 的参数被传入
- 函数 inner 对 func 进行调用,然后装饰器 outer 返回 inner
- 原来的函数名 foo 关联到 inner,如上面的foo 所示,调用 foo 时间上是在调用 inner
装饰器不仅可以用函数返回包装对象,也可以是个类,不过这种方法太尼玛啰嗦,这里就不介绍了,想了解的自己去翻吧。下面我们写一个有点用处的 Decorator。 假想我们有个coordinate类,而且这个类提供了 x, y坐标,而我们要对两个coordinate 对象进行计算。代码如下:
class Coordinate(object): def __init__(self, x, y): self.x = x self.y = y def __repr__(self): return "Coord: " + str(self.__dict__) def add(a, b): return Coordinate(a.x + b.x, a.y + b.y) def sub(a, b): return Coordinate(a.x - b.x, a.y - b.y) In [8]: one = Coordinate(100, 200) In [9]: two = Coordinate(300, 200) In [10]: three = Coordinate(-100, -100) In [11]: sub(one, three) Out[11]: Coord: {'y': 300, 'x': 200} In [12]: add(one, three) Out[12]: Coord: {'y': 100, 'x': 0} In [13]: sub(one, two) Out[13]: Coord: {'y': 0, 'x': -200}
上面例子中的sub(one, two)与three都有负数,当我们把坐标限制在第一象限时,这两个就不符合我们的要求,用 Decorator 来做一个检测再好不过了
In [14]: def wrapper(func): ....: def checker(a, b): ....: if a.x < 0 or a.y < 0: ....: a = Coordinate(a.x if a.x > 0 else 0, a.y if a.y > 0 else 0) ....: if b.x < 0 or b.y < 0: ....: b = Coordinate(b.x if b.x > 0 else 0, b.y if b.y > 0 else 0) ....: ret = func(a, b) ....: if ret.x < 0 or ret.y <0: ....: ret = Coordinate(ret.x if ret.x > 0 else 0, ret.y if ret.y > 0 else 0) ....: return ret ....: return checker ....: In [16]: @wrapper ....: def add(a, b): ....: return Coordinate(a.x + b.x, a.y + b.y) ....: In [17]: @wrapper ....: def sub(a, b): ....: return Coordinate(a.x - b.x, a.y + b.y) ....: In [18]: add(one, three) Out[18]: Coord: {'y': 200, 'x': 100} In [19]: one Out[19]: Coord: {'y': 200, 'x': 100} In [20]: sub(one, two) Out[20]: Coord: {'y': 400, 'x': 0}
这样,只计算的函数add与sub前面加一个 Decorator 就可以完成坐标的校验。比在函数内实现要优雅一些。
Decorator 还可以为类增加额外的成员,
PythonIn [21]: def hello(cls): ....: cls.hello = staticmethod(lambda: "HELLO") ....: return cls ....: In [22]: <a href="http://www.jobbole.com/members/Hello">@hello</a> ....: class World(object):pass ....: In [23]: World.hello Out[23]: <function __main__.<lambda>> In [24]: World.hello() Out[24]: 'HELLO'
functools.wraps
我们在使用 Decorator 的过程中,难免会损失一些原本的功能信息。直接拿 stackoverflow 里面的栗子
def logged(func): def with_logging(*args, **kwargs): print func.__name__ + " was called" return func(*args, **kwargs) return with_logging @logged def f(x): """does some math""" return x + x * x def f(x): """does some math""" return x + x * x f = logged(f) In [24]: f.__name__ Out[24]: with_logging
而functools.wraps 则可以将原函数对象的指定属性复制给包装函数对象, 默认有 __module__、__name__、__doc__,或者通过参数选择。代码如下:
from functools import wraps def logged(func): @wraps(func) def with_logging(*args, **kwargs): print func.__name__ + " was called" return func(*args, **kwargs) return with_logging @logged def f(x): """does some math""" return x + x * x print f.__name__ # prints 'f' print f.__doc__ # prints 'does some math'
相关内容
- 详解Python的装饰器,,为什么需要装饰器 我们假
- Python装饰器的另类用法,python装饰另类,今天我们一起探
- python 装饰器,,业务部门使用基础功能时,
- Python Enclosing作用域、装饰器话聊上篇,pythonenclosing,
- python基础 python类的成员和装饰器,python装饰,这里只着
- python基础 之 装饰器详解,python详解,紧跟装饰器声明的
- 一步步教你理解Python装饰器,python装饰,假定你拥有最基
- Python装饰器之property()详解,pythonproperty,装饰器是解决这
- Python Decorator 基础,pythondecorator,首先来看一个简单的例
- python黑魔法---装饰器(decorator),pythondecorator,装饰器
评论关闭