functools.wraps返回类型为dict的__dict__而不是类型mappingproxy

时间:2017-02-15 23:41:34

标签: python python-3.x python-decorators functools

经过长时间的咨询,这是我的第一个问题。我已经搜索了很多这个问题,我想我没有找到任何有用的信息来解决它。

我正在尝试为Python 3中的代码创建一个通用的弃用python装饰器。目标是正确地包装一个函数或类做两件事:打印关于弃用的警告,并在__doc__前面添加弃用信息(也可由sphinx处理)。我想我已经整理了我想要的大部分内容,我仍然在与Sphinx挣扎,但我发现了对functools.wraps的行为感到好奇。基本上,包装类的__dict__类型为dict,而未展开的类类型为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行为不端还是我做错了什么?

2 个答案:

答案 0 :(得分:0)

Extreme care should be exercised when writing code that uses subgroups if the goal is to write portable OpenCL applications. 不是行为不端也不是你在技术上做错了什么,你已经返回了在被叫时会返回实例的功能。

包装后,functools.wrap是您包装并返回的函数SomeOldClass

new_func

不是班级。未装饰的>>> SomeOldClass <function __main__.SomeOldClass> 将保留为SomeClass__dict__的类。

答案 1 :(得分:0)

我强烈建议让你的包装类知道,所以它不包装类本身:

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

这使您可以在两个类中使用它:

 @Deprecated()
 class SomeOldClass:
     def some_method(self, x, y):
         return x + y

不会模糊类行为和函数(可能使用自定义弃用消息):

 @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,我只是提供基本示例)。