覆盖 python 中@classmethods 的 __str__ 方法

Overriding the __str__ method for @classmethods in python

我试图为 class 覆盖 __str____repr__,如下面的代码所示。每当我调用 instance_method 时都会调用新方法,但对 class_method 的对象调用保持不变(为清楚起见,请参阅下面的代码片段和输出)。有没有一种方法可以覆盖 @classmethod__str____repr__ 以便可以更改 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

Python 不会在 class 本身上寻找 __str__,就像它不会在实例上使用 __str__ 集一样。这适用于 所有 特殊方法,请参阅 Python 数据模型中的 Special method lookup

For custom classes, implicit invocations of special methods are only guaranteed to work correctly if defined on an object’s type, not in the object’s instance dictionary.

简而言之,str(something) 不使用 something.__str__(),它本质上使用 type(something).__str__(something) (*) 正是因为您不想要class 上 __str__ 的定义在您使用 str(class_object) 时会中断,其中 class_object.__str__() 没有要作为 self.[= 传入的实例41=]

您必须定义一个 metaclass,因为那是 'thing' 生成 classes 并由 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 [=71] 的元 class =];现在 type(Abc) returns MetaAbc:

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

MetaAbc.__repr__MetaAbc.__str__用于表示一个class,或将其转换为字符串; class 上的方法在处理实例时使用:

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

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

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

(*) Python 使用 descriptor binding 实现方法,class 方法和静态方法。特殊方法查找直接遍历class层级查找函数对象,避免触发正常的绑定过程,然后手动绑定。所以 str(something) 转换为 next(c.__dict__['__str__'] for c in type(something).__mro__ if '__str__' in c.__dict__).__get__(something, type(something))()。这有点啰嗦,对于 normal 方法,这可以简化为 type(something).__str__(something),因为它具有相同的效果。