我也偶然发现了这一点,我真的很惊讶,对我来说问题是部分对象缺少某些属性,特别是__module__和__name__
默认使用functools.WRAPPER_ASSIGNMENTS来更新属性,默认为python 2.7.6中的”__module __,’_ _ _ _ _ _ _ _’,’_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ….
仅更新当前属性…
import functools
import itertools
def wraps_safely(obj, attr_names=functools.WRAPPER_ASSIGNMENTS):
return wraps(obj, assigned=itertools.ifilter(functools.partial(hasattr, obj), attr_names))
>>> def foo():
... """ Ubiquitous foo function ...."""
...
>>> functools.wraps(partial(foo))(foo)()
Traceback (most recent call last):
File "", line 1, in
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/functools.py", line 33, in update_wrapper
setattr(wrapper, attr, getattr(wrapped, attr))
AttributeError: 'functools.partial' object has no attribute '__module__'
>>> wraps_safely(partial(foo))(foo)()
>>>
在这里,我们只是过滤掉所有不存在的属性.
另一种方法是仅严格处理部分对象,您可以使用singledispatch折叠包装并创建包含的部分对象,其属性将从最深的func属性中获取.
一些事情:
import functools
def wraps_partial(wrapper, *args, **kwargs):
""" Creates a callable object whose attributes will be set from the partials nested func attribute ..."""
wrapper = wrapper.func
while isinstance(wrapper, functools.partial):
wrapper = wrapper.func
return functools.wraps(wrapper, *args, **kwargs)
def foo():
""" Foo function.
:return: None """
pass
>>> wraps_partial(partial(partial(foo)))(lambda : None).__doc__
' Foo Function, returns None '
>>> wraps_partial(partial(partial(foo)))(lambda : None).__name__
'foo'
>>> wraps_partial(partial(partial(foo)))(lambda : None)()
>>> pfoo = partial(partial(foo))
>>> @wraps_partial(pfoo)
... def not_foo():
... """ Not Foo function ... """
...
>>> not_foo.__doc__
' Foo Function, returns None '
>>> not_foo.__name__
'foo'
>>>
这稍微好一点,因为现在我们可以获得默认使用部分对象文档字符串之前的原始函数文档.
这可以修改为仅搜索当前部分对象是否还没有set属性,嵌套多个部分对象时应该稍快一些…
UPDATE
似乎python(CPython)3(至少3.4.3)没有这个问题,因为我不知道也不应该假设所有版本的python 3或其他实现如Jython也在这里分享这个问题是另一个未来的准备方法
from functools import wraps, partial, WRAPPER_ASSIGNMENTS
try:
wraps(partial(wraps))(wraps)
except AttributeError:
@wraps(wraps)
def wraps(obj, attr_names=WRAPPER_ASSIGNMENTS, wraps=wraps):
return wraps(obj, assigned=(name for name in attr_names if hasattr(obj, name)))
有几点需要注意:
>我们定义一个新的包装函数,只有当我们无法包装部分时,以防python2或其他版本的未来版本修复此问题.
>我们使用原始包装来复制文档和其他信息
>我们不使用ifilter,因为它已经在python3中删除了,我有时间和没有ifilter,但结果是不确定的,至少在python(CPython)2.7.6中,差异在最好的方式是最小的……