5

我试图覆盖__str____repr__for 类,如下面的代码所示。每当我调用 instance_method 时都会调用新方法,但对 class_method 的对象调用保持不变(为清楚起见,请参阅下面的代码片段和输出)。有没有办法覆盖__str__and__repr__以便可以更改@classmethod的值?cls

我也尝试过添加,__str__但没有任何改变。__repr__@classmethod

class Abc:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return f"Added {self.name}"

    def __repr__(self):
        return f"instance method repr"

    def instance_method(self):
        print(f"instance method {self}")

    @classmethod
    def __repr__(cls):
        return f"class method"

    @classmethod
    def __str__(cls):
        return f"class method"

    @classmethod
    def class_method(cls):
        print(f"class method '{cls}'")

    @staticmethod
    def static_method():
        print(f"static method")

    def add(self, a: int,b: int,c: int) -> int:
        return a+b+c


o = Abc("alpha")
o.class_method()
o.static_method()
o.instance_method()
Abc.static_method()
Abc.class_method()
print(o.add(1,2,3))

上述代码的输出:

class method '<class '__main__.Abc'>'
static method
instance method class method
static method
class method '<class '__main__.Abc'>'
6
4

1 回答 1

10

Python 不会__str__在类本身上查找 a,就像它不会__str__在实例上使用 set 一样。这适用于所有特殊方法,请参阅Python 数据模型中的特殊方法查找:

对于自定义类,特殊方法的隐式调用只有在对象类型上定义时才能保证正常工作,而不是在对象的实例字典中。

简而言之,str(something)不使用something.__str__(),它本质上使用type(something).__str__(something) (*)正是因为您不希望在__str__使用时破坏类的定义str(class_object),其中class_object.__str__()没有实例可以作为 传递self

您必须定义一个元类,因为那是创建类并由以下方式返回的“事物” type(class_object)

class MetaAbc(type):
    def __repr__(cls):
        return "__repr__ on the metaclass"

    def __str__(cls):
        return "__str__ on the metaclass"

class Abc(metaclass=MetaAbc):
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return f"Added {self.name}"

    def __repr__(self):
        return "instance method repr"

metaclass=MetaAbc语法告诉 Python 使用而MetaAbc不是仅仅type作为类的元Abc类;现在type(Abc)返回MetaAbc

>>> type(Abc)
<class '__main__.MetaAbc'>

和用于表示类或将其转换为字符串时使用MetaAbc.__repr__MetaAbc.__str__处理实例时使用类上的方法:

>>> Abc
__repr__ on the metaclass
>>> print(Abc)
__str__ on the metaclass
>>> Abc('foo')
instance method repr
>>> print(Abc('foo'))
Added foo

装饰器不会将@classmethod方法放入不同的命名空间;类方法是类的普通属性,只是以不同的方式绑定。@classmethod例如,在实例上仍然可以访问,但总是会传递给类对象,即使通过实例访问也是如此:

>>> Abc.class_method()
class method '__str__ on the metaclass'
>>> Abc("foo").class_method()
class method '__str__ on the metaclass'

(*) Python 使用描述符绑定来实现方法、类方法和静态方法。特殊方法查找直接通过遍历类层次结构查找函数对象,避免触发正常的绑定过程,然后手动绑定。所以str(something)翻译为next(c.__dict__['__str__'] for c in type(something).__mro__ if '__str__' in c.__dict__).__get__(something, type(something))(). 这有点拗口,对于正常的方法,这可以简化type(something).__str__(something)为具有相同的效果。

于 2019-09-12T17:17:15.917 回答