Python-进阶-functools模块介绍

functools.partial

functools.partial 通过包装手法,允许我们 "重新定义" 函数签名
用一些默认参数包装一个可调用对象,返回结果是可调用对象,并且可以像原始对象一样对待
冻结部分函数位置函数或关键字参数,简化函数,更少更灵活的函数参数调用
应用:
典型的,函数在执行时,要带上所有必要的参数进行调用。
然后,有时参数可以在函数被调用之前提前获知。
这种情况下,一个函数有一个或多个参数预先就能用上,以便函数能用更少的参数进行调用。

import functools

def add(a, b):
    return a + b

if __name__ == '__main__':
    print add(3, 4)
    plus9 = functools.partial(add, 9)
    print plus9(11)
    plus7 = functools.partial(add, 5)
    print plus7(4)
    
C:\Python27\python.exe D:/wangyueWorkspace/mytest/functiontools/test1.py
7
20
9

functool.update_wrapper

默认partial对象没有namedoc

def wrap(func):
    def call_it(*args, **kwargs):
        """wrap func: call it"""
        print 'before call'
        return func(*args, **kwargs)
    return call_it

@wrap
def hello():
    """hello"""
    print 'hello world!'
    
if __name__ == '__main__':
    hello()
    print hello.__name__
    print hello.__doc__
    
C:\Python27\python.exe D:/wangyueWorkspace/mytest/functiontools/test2.py
before call
hello world!
call_it
wrap func: call it

使用update_wrapper可以把被封装函数的name、module、docdict都复制到封装函数去(模块级别常量WRAPPER_ASSIGNMENTS, WRAPPER_UPDATES)


from functools import update_wrapper

def wrap2(func):
    def call_it(*args, **kwargs):
        """wrap2 func: call it"""
        print 'before call2'
        return func(*args, **kwargs)
    return update_wrapper(call_it, func)

@wrap2
def hello2():
    """hello2 test
    aaaaaaaaaaaaa
    asdfasdfad
    """
    print 'hello world2!'


if __name__ == '__main__':
    hello2()
    print hello2.__name__
    print hello2.__doc__
    
C:\Python27\python.exe D:/wangyueWorkspace/mytest/functiontools/test2.py  
before call2
hello world2!
hello2
hello2 test
aaaaaaaaaaaaa
asdfasdfad

这个函数主要用在装饰器函数中,装饰器返回函数反射得到的是包装函数的函数定义而不是原始函数定义

functool.wraps

调用函数装饰器partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated)的简写。

from functools import wraps, update_wrapper

def mywrap(func):
    @wraps(func)
    def call_it(*args, **kwargs):
        """wrap func: call_it2"""
        print 'before call'
        return func(*args, **kwargs)

    update_wrapper(call_it, func)
    return call_it


@mywrap
def hello():
    """test hello"""
    print 'hello'


if __name__ == '__main__':
    hello()
    print hello.__name__
    print hello.__doc__

运行效果可知,functool.wraps装饰器和update_wrapper函数其实效果是一样的,都是把被封装函数的__name__、module、__doc__和 __dict__都复制到封装函数去。

问题:

==但是很奇怪,在cinder的代码里,同时用了这两个东西,不懂事为什么???==

def _retry_on_deadlock(f):
    """Decorator to retry a DB API call if Deadlock was received."""

    @functools.wraps(f)
    def wrapped(*args, **kwargs):
        while True:
            try:
                return f(*args, **kwargs)
            except db_exc.DBDeadlock:
                LOG.warning(_LW("Deadlock detected when running "
                                "'%(func_name)s': Retrying..."),
                            dict(func_name=f.__name__))
                # Retry!
                time.sleep(0.5)
                continue

    functools.update_wrapper(wrapped, f)
    return wrapped

这里补充下:
__name__、__doc__和 __dict__的作用:

  1. 在python中,当一个module作为整体被执行时,moduel.__name__`的值将是"__ main__";而当一个 module被其它module引用时,module.__name__将是module自己的名字
>>> print dir.__name__
dir
>>> print __name__
__main__
  1. 每个函数都是一个对象,每个函数对象都是有一个__doc__的属性,函数语句中,如果第一个表达式是一个string,这个函数的__doc__就是这个string,否则__doc__是None。其实就是函数注释~~
>>> def hello():
...     """hello function lalalal"""
...     print 'hello world'
...
>>>
>>> print hello.__doc__
hello function lalalal
  1. __dict__是一个字典,键是属性名,值为属性值。Python的实例有自己的__dict__,它对应的类也有自己的__dict__
>>> print file.__dict__
{'softspace': <attribute 'softspace' of 'file' objects>, 'encoding': <member 'encoding' of 'file' objects>, 'xreadlines': <method 'xreadlines' of 'file' objects>, 'readlines': <method 'readlines' of 'file' objects>, 'flush': <method 'flush' of 'file' objects>, 'close': <method 'close' of 'file' objects>, 'seek': <method 'seek' of 'file' objects>, '__init__': <slot wrapper '__init__' of 'file' objects>, 'newlines': <attribute 'newlines' of 'file' objects>, '__setattr__': <slot wrapper '__setattr__' of 'file' objects>, 'errors': <member 'errors' of 'file' objects>, '__new__': <built-in method __new__ of type object at 0x7f220ba9cd20>, 'readinto': <method 'readinto' of 'file' objects>, '__enter__': <method '__enter__' of 'file' objects>, 'next': <slot wrapper 'next' of 'file' objects>, 'write': <method 'write' of 'file' objects>, 'closed': <attribute 'closed' of 'file' objects>, 'tell': <method 'tell' of 'file' objects>, 'mode': <member 'mode' of 'file' objects>, '__exit__': <method '__exit__' of 'file' objects>, 'isatty': <method 'isatty' of 'file' objects>, 'truncate': <method 'truncate' of 'file' objects>, 'read': <method 'read' of 'file' objects>, '__getattribute__': <slot wrapper '__getattribute__' of 'file' objects>, '__iter__': <slot wrapper '__iter__' of 'file' objects>, 'readline': <method 'readline' of 'file' objects>, 'fileno': <method 'fileno' of 'file' objects>, 'writelines': <method 'writelines' of 'file' objects>, 'name': <member 'name' of 'file' objects>, '__doc__': "file(name[, mode[, buffering]]) -> file object\n\nOpen a file.  The mode can be 'r', 'w' or 'a' for reading (default),\nwriting or appending.  The file will be created if it doesn't exist\nwhen opened for writing or appending; it will be truncated when\nopened for writing.  Add a 'b' to the mode for binary files.\nAdd a '+' to the mode to allow simultaneous reading and writing.\nIf the buffering argument is given, 0 means unbuffered, 1 means line\nbuffered, and larger numbers specify the buffer size.  The preferred way\nto open a file is with the builtin open() function.\nAdd a 'U' to mode to open the file for input with universal newline\nsupport.  Any line ending in the input file will be seen as a '\\n'\nin Python.  Also, a file so opened gains the attribute 'newlines';\nthe value for this attribute is one of None (no newline read yet),\n'\\r', '\\n', '\\r\\n' or a tuple containing all the newline types seen.\n\n'U' cannot be combined with 'w' or '+' mode.\n", '__delattr__': <slot wrapper '__delattr__' of 'file' objects>, '__repr__': <slot wrapper '__repr__' of 'file' objects>}

参考:

Python-进阶-functools模块小结

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,227评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,755评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,899评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,257评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,617评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,757评论 1 221
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,982评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,715评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,454评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,666评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,148评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,512评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,156评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,112评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,896评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,809评论 2 279
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,691评论 2 272

推荐阅读更多精彩内容