Skip to main content
 首页 » 编程设计

python之__repr__ 装饰器在子类上失败

2025年05月04日23findumars

背景

我编写了一个修饰函数来修改给定类的 __repr__,这样当调用一个类实例时,它的所有属性都会打印给用户。当在下面示例中的 Container 类中使用时,装饰器 __repr__dec 的行为符合预期。

输入

def __repr__wrapper(self): 
    """Show all attributes.""" 
    return "Attributes: "+", ".join(list(self.__dict__.keys())) 
 
def __repr__dec(func): 
    """Replaces the __repr__ function of a class with __repr__wrapper""" 
    def call(*args, **kwargs): 
        func.__repr__ = __repr__wrapper 
        result = func(*args, **kwargs) 
        return result 
    return call 
 
 
@__repr__dec 
class Container(object): 
    def __init__(self, *args, **kwargs): 
        self.metadata = args[0] 
        for k,v in kwargs.items(): 
            self.__dict__[k] = v 
 
occ = Container(42, how="now") 
occ 

输出

Attributes: metadata, how 

然而,当尝试子类化 Container 时,我收到一条 TypeError 消息:

输入

class Handle(Container): 
    def __init__(self, *args, **kwargs): 
        Container.__init__(self, *args, **kwargs) 
 
han = Handle(42) 

输出

TypeError                                 Traceback (most recent call last) 
<ipython-input-17-b4c252411c1f> in <module>() 
----> 1 class Handle(Container): 
      2     def __init__(self, *args, **kwargs): 
      3         Container.__init__(self, *args, **kwargs) 
      4  
      5 han = Handle(42) 
 
TypeError: function() argument 1 must be code, not str 

问题

为什么在使用 __repr__dec 函数时子类化 Conatainer 会失败?有可能解决这个问题吗?

请您参考如下方法:

问题是您的装饰器使 Container 成为一个函数而不再是一个类。您可以非常简单地控制它:

>>> type(Container) 
<class 'function'> 

这是因为你对装饰器的使用结束于:

  • 声明一个未修饰的类

    class Container: 
        ... 
    
  • 在上面使用装饰器:

    Container = __repr__dec(Container) 
    

由于 __repr__dec 返回一个函数,您确实将 Container 更改为能够返回具有预期 __repr__ 成员的对象的函数,但它是不再是一个类。

如果您希望以后能够对其进行子类化,您的装饰器必须返回一个类:

def repr_dec(cls): 
    cls.__repr__ = __repr__wrapper 
    return cls 

然后一切正常:

>>> Container 
<class '__main__.Container'> 
>>> occ=Container(42, how="now") 
>>> occ 
Attributes: metadata, how 

并且您可以成功地对其进行子类化:

>>> class Handle(Container): 
    def __init__(self, *args, **kwargs): 
        Container.__init__(self, *args, **kwargs) 
 
>>> han = Handle(42, foo="now") 
>>> han 
Attributes: metadata, foo 

Handle 类从其父类继承了 __repr__ 方法。