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对象没有name和doc
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、doc和 dict都复制到封装函数去(模块级别常量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__
的作用:
- 在python中,当一个module作为整体被执行时,moduel.__name__`的值将是"__ main__";而当一个 module被其它module引用时,module.__name__将是module自己的名字
>>> print dir.__name__
dir
>>> print __name__
__main__
- 每个函数都是一个对象,每个函数对象都是有一个__doc__的属性,函数语句中,如果第一个表达式是一个string,这个函数的__doc__就是这个string,否则__doc__是None。其实就是函数注释~~
>>> def hello():
... """hello function lalalal"""
... print 'hello world'
...
>>>
>>> print hello.__doc__
hello function lalalal
-
__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>}
网友评论