functools.wraps相当于类装饰器

时间:2015-02-20 05:31:10

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

当我们装饰功能时,我们使用functools.wraps使装饰功能看起来像原始。

当我们想要装饰类时,是否有任何扫管笏做同样的事情?

def some_class_decorator(cls_to_decorate):
    class Wrapper(cls_to_decorate):
        """Some Wrapper not important doc."""
        pass
    return Wrapper


@some_class_decorator
class MainClass:
    """MainClass important doc."""
    pass


help(MainClass)

输出:

class Wrapper(MainClass)
 |  Some Wrapper not important doc.
 |  
 |  Method resolution order:
 |      Wrapper
 |      MainClass
 |      builtins.object
 |  
 # ... no MainClass important doc below.

我尝试根据functools.wraps源代码编写与类装饰器相当的包装,但我的实现并不正确:

import functools


def wraps_cls(original_cls):
    def wrapper(wrapper_cls):
        """Update wrapper_cls to look like original_cls."""
        for attr in functools.WRAPPER_ASSIGNMENTS:
            try:
                value = getattr(original_cls, attr)
            except AttributeError:
                pass
            else:
                setattr(wrapper_cls, attr, value)
        return wrapper_cls
    return wrapper


def some_class_decorator(cls_to_decorate):
    @wraps_cls(cls_to_decorate)
    class Wrapper(cls_to_decorate):
        """Some Wrapper not important doc."""
        pass
    return Wrapper


@some_class_decorator
class MainClass:
    """MainClass important doc."""
    pass


help(MainClass)

输出:

class MainClass(MainClass)
 |  MainClass important doc.
 |  
 |  Method resolution order:
 |      MainClass
 |      MainClass
 |      builtins.object
 |  
 # ... MainClass doc is here but "Method resolution order" is broken.

是否有完全用未装饰的MainClass帮助输出替换装饰的MainClass帮助输出?

2 个答案:

答案 0 :(得分:2)

不,没有,假设您的装饰器确实是像some_class_decorator这样的包装类的子类。 help的输出由pydoc.Helper类定义,类最终调用pydoc.text.docclass,其中包含以下代码:

# List the mro, if non-trivial.
mro = deque(inspect.getmro(object))
if len(mro) > 2:
    push("Method resolution order:")
    for base in mro:
        push('    ' + makename(base))
    push('')

你可以看到它是硬编码的,可以显示班级真正的MRO。这是应该的。您上一个示例中显示的MRO不是"已损坏",它是正确的。通过使包装类继承自包装类,您向继承层次结构添加了一个附加类。如果显示一个MRO将其排除在外是错误的,因为那里确实有一个类。在你的例子中,这个包装类并没有提供它自己的任何行为,但是一个真实的包装类会(或者你为什么要进行包装?),你会想知道哪个行为来自包装类和包装类中的类。

如果你愿意,你可以制作一个动态重命名包装类的装饰器,其名称来自原始名称,因此MRO会在适当的位置显示类似DecoratorWrapper_of_MainClass的内容。这是否比Wrapper更具可读性还存在争议。

答案 1 :(得分:1)

哦,我想现在我明白你想要达到的目标。

您想要从"包装器"添加新方法。使用类装饰器。

这是一个工作示例:

class Wrapper(object):
    """Some Wrapper not important doc."""
    def another_method(self):
        """Another method."""
        print 'Another method'


def some_class_decorator(cls_to_decorate):
    return type(cls_to_decorate.__name__, cls_to_decorate.__bases__, dict
        (cls_to_decorate.__dict__, another_method=Wrapper.__dict__['another_method']))


class MainClass(object):
    """MainClass important doc."""
    def method(self):
        """A method."""
        print "A method"


help(MainClass)
_MainClass = some_class_decorator(MainClass)
help(_MainClass)
_MainClass().another_method()
MainClass().another_method()

此示例创建一个不修改旧类的新类。

但我想你也可以在旧类中注入给定的方法,并在适当的位置修改它。