functools.wraps 返回一个 __dict__ 类型的 dict 而不是类型 mappingproxy
functools.wraps returning a __dict__ of type dict instead of type mappingproxy
这是我的第一个问题,经过长时间的咨询。我在谷歌上搜索了很多关于这个问题的信息,但我想我没有找到解决它的任何有用信息。
我正在尝试为我在 Python 3 中的代码创建一个通用的弃用 python 装饰器。 objective 是正确包装一个函数或 class 做两个事情:打印关于弃用的警告,并在 __doc__
前面加上弃用信息(它也可以由 sphinx
处理)。我想我已经整理出了大部分我想要的东西,我仍然在与 Sphinx 作斗争,但我发现了一些对 functools.wraps
的行为感到好奇的东西。基本上,包装的 class 的 __dict__
是 dict
类型,而未包装的 class 的类型是 mappingproxy
.
这是一些展示代码:
import functools
class Deprecated(object):
def __call__(self, cls):
myassigns = functools.WRAPPER_ASSIGNMENTS
myassigns += ('__mro__',)
@functools.wraps(cls, assigned=myassigns)
def new_func(*args, **kwargs):
warnings.warn(message, category=DeprecationWarning, stacklevel=2)
return cls(*args, **kwargs)
return new_func
@Deprecated()
class SomeOldClass:
def some_method(self, x, y):
return x + y
class SomeClass:
def some_method(self, x, y):
return x + y
print(type(SomeOldClass.__dict__))
print(type(SomeClass.__dict__))
并且输出:
<class 'dict'>
<class 'mappingproxy'>
是 functools.wraps
行为不当还是我做错了什么?
functools.wrap
没有行为不端,您在技术上也没有做错什么,您已经 return 编辑了 调用时 将 return 实例。
包装后,SomeOldClass
是您包装并 return 编辑的函数 new_func
:
>>> SomeOldClass
<function __main__.SomeOldClass>
不是 class。 un-decorated SomeClass
被保存为 class 谁的 __dict__
是 mappingproxy
.
我强烈建议让你的包装器 class 知道,这样它就不会包装 class 本身:
class Deprecated(object):
def __init__(self, messagefmt='{wrapped} is deprecated'):
self.messagefmt = messagefmt
def __call__(self, wrapped):
message = self.messagefmt.format(wrapped=wrapped.__name__)
wrappingclass = isinstance(wrapped, type)
if wrappingclass:
wrappedfunc = wrapped.__init__
else:
wrappedfunc = wrapped
@functools.wraps(wrappedfunc)
def new_func(*args, **kwargs):
warnings.warn(message, category=DeprecationWarning, stacklevel=2)
return wrappedfunc(*args, **kwargs)
wrapped.__doc__ = 'Deprecated\n\n' + message + '\n\n' + (wrapped.__doc__ or '')
if wrappingclass:
wrapped.__init__ = new_func
return wrapped
else:
return new_func
这让您可以将它与 classes 一起使用:
@Deprecated()
class SomeOldClass:
def some_method(self, x, y):
return x + y
不掩盖 class 行为和功能(可能带有自定义弃用消息):
@Deprecated('foo is dead')
def foo():
pass
所以当你使用它们时:
>>> import warnings
>>> warnings.filterwarnings('always', category=DeprecationWarning)
>>> SomeOldClass()
/usr/local/bin/ipython3:1: DeprecationWarning: SomeOldClass is deprecated
#!/usr/bin/python3
<__main__.SomeOldClass at 0x7fea8f744a20>
>>> foo()
/usr/local/bin/ipython3:1: DeprecationWarning: foo is dead
#!/usr/bin/python3
同样,两者的 __doc__
都被修改为在弃用通知之前添加(您可以将其调整为 Sphinxify,我只是提供了基本示例)。
这是我的第一个问题,经过长时间的咨询。我在谷歌上搜索了很多关于这个问题的信息,但我想我没有找到解决它的任何有用信息。
我正在尝试为我在 Python 3 中的代码创建一个通用的弃用 python 装饰器。 objective 是正确包装一个函数或 class 做两个事情:打印关于弃用的警告,并在 __doc__
前面加上弃用信息(它也可以由 sphinx
处理)。我想我已经整理出了大部分我想要的东西,我仍然在与 Sphinx 作斗争,但我发现了一些对 functools.wraps
的行为感到好奇的东西。基本上,包装的 class 的 __dict__
是 dict
类型,而未包装的 class 的类型是 mappingproxy
.
这是一些展示代码:
import functools
class Deprecated(object):
def __call__(self, cls):
myassigns = functools.WRAPPER_ASSIGNMENTS
myassigns += ('__mro__',)
@functools.wraps(cls, assigned=myassigns)
def new_func(*args, **kwargs):
warnings.warn(message, category=DeprecationWarning, stacklevel=2)
return cls(*args, **kwargs)
return new_func
@Deprecated()
class SomeOldClass:
def some_method(self, x, y):
return x + y
class SomeClass:
def some_method(self, x, y):
return x + y
print(type(SomeOldClass.__dict__))
print(type(SomeClass.__dict__))
并且输出:
<class 'dict'>
<class 'mappingproxy'>
是 functools.wraps
行为不当还是我做错了什么?
functools.wrap
没有行为不端,您在技术上也没有做错什么,您已经 return 编辑了 调用时 将 return 实例。
包装后,SomeOldClass
是您包装并 return 编辑的函数 new_func
:
>>> SomeOldClass
<function __main__.SomeOldClass>
不是 class。 un-decorated SomeClass
被保存为 class 谁的 __dict__
是 mappingproxy
.
我强烈建议让你的包装器 class 知道,这样它就不会包装 class 本身:
class Deprecated(object):
def __init__(self, messagefmt='{wrapped} is deprecated'):
self.messagefmt = messagefmt
def __call__(self, wrapped):
message = self.messagefmt.format(wrapped=wrapped.__name__)
wrappingclass = isinstance(wrapped, type)
if wrappingclass:
wrappedfunc = wrapped.__init__
else:
wrappedfunc = wrapped
@functools.wraps(wrappedfunc)
def new_func(*args, **kwargs):
warnings.warn(message, category=DeprecationWarning, stacklevel=2)
return wrappedfunc(*args, **kwargs)
wrapped.__doc__ = 'Deprecated\n\n' + message + '\n\n' + (wrapped.__doc__ or '')
if wrappingclass:
wrapped.__init__ = new_func
return wrapped
else:
return new_func
这让您可以将它与 classes 一起使用:
@Deprecated()
class SomeOldClass:
def some_method(self, x, y):
return x + y
不掩盖 class 行为和功能(可能带有自定义弃用消息):
@Deprecated('foo is dead')
def foo():
pass
所以当你使用它们时:
>>> import warnings
>>> warnings.filterwarnings('always', category=DeprecationWarning)
>>> SomeOldClass()
/usr/local/bin/ipython3:1: DeprecationWarning: SomeOldClass is deprecated
#!/usr/bin/python3
<__main__.SomeOldClass at 0x7fea8f744a20>
>>> foo()
/usr/local/bin/ipython3:1: DeprecationWarning: foo is dead
#!/usr/bin/python3
同样,两者的 __doc__
都被修改为在弃用通知之前添加(您可以将其调整为 Sphinxify,我只是提供了基本示例)。