在看 Bottle 代码中看见 functools.wraps 这种用法。

def make_default_app_wrapper(name):
""" Return a callable that relays calls to the current default app. """
a = getattr(Bottle, name)
@functools.wraps(getattr(Bottle, name))
def wrapper(*a, **ka):
return getattr(app(), name)(*a, **ka)
return wrapper

之前没有看过,于是查文档了解了一下他的用处 先下定义: functools.wraps 是 ``装饰器``的``装饰器``

要明白 functiools.wraps 首先要明白 Python 的 Decorator

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

这个过程中执行了下面几步

  1. 函数 foo 作为 装饰器 outer 的参数被传入
  2. 函数 inner 对 func 进行调用,然后装饰器 outer 返回 inner
  3. 原来的函数名 foo 关联到 inner,如上面的foo <function __main__.inner> 所示,调用 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}

这样,只计算的函数addsub前面加一个 Decorator 就可以完成坐标的校验。比在函数内实现要优雅一些。

Decorator 还可以为类增加额外的成员,

In [21]: def hello(cls):
....: cls.hello = staticmethod(lambda: "HELLO")
....: return cls
....: In [22]: @hello
....: 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'
from:http://sunisdown.me/python-zhuang-shi-qi-zhi-functoolswraps.html

Python 装饰器之 functools.wraps的更多相关文章

  1. Python装饰器之functools.wraps的作用

    # -*- coding: utf-8 -*- # author:baoshan def wrapper(func): def inner_function(): pass return inner_ ...

  2. python装饰器中@wraps作用--修复被装饰后的函数名等属性的改变

    Python装饰器(decorator)在实现的时候,被装饰后的函数其实已经是另外一个函数了(函数名等函数属性会发生改变),为了不影响,Python的functools包中提供了一个叫wraps的de ...

  3. python装饰器的wraps作用

    不加: from functools import wraps def my_decorator(func): def wper(*args, **kwargs): '''decorator''' p ...

  4. Python装饰器之 property()

    1. 何为装饰器? 官方定义:装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志.性能测试.事务处理等.装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数 ...

  5. python 装饰器之应用示例

    import time import hashlib import pickle import threading #装饰函数缓存应用 cache ={} def is_obsolete(entry, ...

  6. python装饰器之函数作用域

    1.函数作用域LEGB L:local函数内部作用域 E:enclosing函数内部与内嵌函数之间 G:global全局作用域 B:build-in内置作用域 passline = 60 def fu ...

  7. python装饰器之使用情景分析

    http://blog.csdn.net/yueguanghaidao/article/details/10089181

  8. Python装饰器详解

    python中的装饰器是一个用得非常多的东西,我们可以把一些特定的方法.通用的方法写成一个个装饰器,这就为调用这些方法提供一个非常大的便利,如此提高我们代码的可读性以及简洁性,以及可扩展性. 在学习p ...

  9. Python 装饰器学习心得

    最近打算重新开始记录自己的学习过程,于是就捡起被自己废弃了一年多的博客.这篇学习笔记主要是记录近来看的有关Python装饰器的东西. 0. 什么是装饰器? 本质上来说,装饰器其实就是一个特殊功能的函数 ...

随机推荐

  1. bash shell:获取当前脚本的绝对路径(pwd/readlink)

    有时候,我们需要知道当前执行的输出shell脚本的所在绝对路径,可以用dirname实现. 我们知道 dirname 可以获取一个文件所在的路径,dirname的用处是: 输出已经去除了尾部的”/”字 ...

  2. Navicat Premium 12.1.20.0安装与激活

    一.Navicat Premium 12下载 链接: https://pan.baidu.com/s/1GgNbCPGahN-Z91f4dnQkBQ 提取码: 3q8f 复制这段内容后打开百度网盘手机 ...

  3. 创建DSN

    DSN:ata Source Name (DSN)的PDO命名惯例为:PDO驱动程序的名称,后面为一个冒号,再后面是可选的驱动程序连接数据库变量信息,如主机名.端口和数据库名. 有三种类型的DSN,三 ...

  4. oracle--序列&视图&索引&视图&可视化操作&分页&数据库备份

    --oracle学习内容--oracle的管理系统学习--oracle的数据管理学习--oracle的用户管理--oracle二维表管理--oracle的其他知识 --oracle的序列.视图.索引 ...

  5. POJ 1905 题解(二分+几何)

    题面 传送门 分析 如图:已知AB=L,弧AB=L(1+nC)" role="presentation" style="position: relative;& ...

  6. P3188 [HNOI2007]梦幻岛宝珠

    传送门 注意到 $a,b$ 不大 考虑对每一个 $a*2^b$ 的 $b$ 分别背包 设 $f[i][j]$ 表示只考虑 $b=i$ 的物品时,容量为 $j= \sum a$ 的最大价值 这个就是普通 ...

  7. CSS高度坍塌原因及解决办法

    在文档流中,父元素的高度默认是被子元素撑开的,也就是子元素多高,父元素就多高. 但是当为子元素设置浮动以后,子元素会完全脱离文档流,此时将会导致子元素无法撑起父元素的高度,导致父元素的高度塌陷.由于父 ...

  8. Android Studio 于夜神模拟器进行连接

    本文使用夜神模拟器自带的nox_adb.exe在Android Studio中连接夜神模拟器. 1.在夜神模拟器的安装路径下,在bin文件夹下有个nox_adb.exe文件,复制黄色框路径如图: 2. ...

  9. .NET File 多图上传

    HTML代码: <div> <div> <input type="file" style="display:none" id=&q ...

  10. SVG 学学就会了。

    SVG 随便学学就会了 这两天闲来没事把 Echart 换成 Rechart 感觉世界都清爽了.因为 rechart 使用 svg 来渲染,所以顺带学了下 SVG 感觉很轻松哦. 概念 SVG 是 w ...