functools.wraps相当于类装饰器

Mik*_*mov 9 python python-3.x python-decorators

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

当我们想要装饰类时,有没有wat做同样的事情?

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)
Run Code Online (Sandbox Code Playgroud)

输出:

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

我尝试根据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)
Run Code Online (Sandbox Code Playgroud)

输出:

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

反正有没有完全替换装饰的MainClass帮助输出与未装饰的MainClass帮助输出?

Bre*_*arn 5

不,没有,假设你的装饰器真的像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('')
Run Code Online (Sandbox Code Playgroud)

您可以看到它是硬编码来显示类的真实 MRO。这是应该的。上一个示例中显示的 MRO 没有“损坏”,它是正确的。通过使包装类从包装类继承,您向继承层次结构中添加了一个附加类。显示遗漏这一点的 MRO 会产生误导,因为那里确实有一个类。在您的示例中,此包装器类不提供其自己的任何行为,但现实的包装器类会提供(否则为什么您要进行包装?),并且您想知道哪些行为来自包装器类以及来自包装类的类。

如果需要,您可以创建一个装饰器,使用源自原始名称的名称动态重命名包装类,这样 MRO 就会DecoratorWrapper_of_MainClass在适当的位置显示类似的内容。这是否比仅仅拥有Wrapper它更具可读性是有争议的。

  • @GerasimovMikhail:但这是一个单独的问题。编写一个显示 MainClass 的 __doc__ 的包装器并不难;正如您所看到的,您现有的解决方案已经做到了这一点。但是“help”向您显示的不仅仅是“__doc__”,而且您不能干扰其他输出;你只控制`__doc__`。 (2认同)