在 set 的子类上调用 super().__repr__() 时出现意外行为?

Unexpected behavior when calling super().__repr__() on a subclass of set?

请参阅下面的编辑以了解此行为的解释(在 CPython 中)

在 Python 3.9.5 中,我创建了两种自定义集合类型,一种继承自 list,另一种继承自 set。我希望他们有自定义 __repr__ 方法。简单示例:

class MyList(list):

    def __repr__(self) -> str:
        return f'MyList({super().__repr__()})'


class MySet(set):

    def __repr__(self) -> str:
        return f'MySet({super().__repr__()})'

请注意 __repr__() 定义除了初始字符串前缀外是相同的。但是当我打印它们时,我得到了不同的结果:

ml = MyList('abc')
print(ml) # MyList(['a', 'b', 'c'])

ms = MySet('abc')
print(ms) # MySet(MySet({'c', 'a', 'b'}))

MyList 的显示是我所期望的。但是由于某种原因 MySet 被打印了两次,就好像 __repr__ 被递归调用一样。有谁知道发生了什么,或者如何最好地让它显示 MySet({'c', 'a', 'b'}),而不是 MySet(MySet({'c', 'a', 'b'}))

[编辑] 从 3.9.5 开始,在 https://github.com/python/cpython/blob/main/Objects/setobject.c 函数 set_repr 中有代码:

    if (!PySet_CheckExact(so))
        result = PyUnicode_FromFormat("%s({%U})",
                                      Py_TYPE(so)->tp_name,
                                      listrepr);
    else
        result = PyUnicode_FromFormat("{%U}", listrepr);

换句话说,set 是硬编码的,如果类型恰好是 set 且集合非空,则其 __repr__ 会省略 class 名称, 但以其他方式显示 class 名称。但是 dictlist 出于某种原因不这样做。

这是set的默认打印,它打印class名字。如果您删除您添加的 format,它就会完全按照您的意愿出现。

class MySet(set):

    def __repr__(self) -> str:
        return super().__repr__()

if __name__ == '__main__':

    ms = MySet('abc')
    print(ms)
    MySet.__name__ = 'New_name'
    print(ms)

输出:

MySet({'b', 'c', 'a'})
New_name({'b', 'c', 'a'})