See edit below for an explanation of this behavior (in CPython)
In Python 3.9.5, I'm creating two custom collection types, one inheriting from list and the other from set. I want them to have custom __repr__ methods. Simple example:
class MyList(list):
def __repr__(self) -> str:
return f'MyList({super().__repr__()})'
class MySet(set):
def __repr__(self) -> str:
return f'MySet({super().__repr__()})'
Note the __repr__() definitions are identical except for the initial string prefix. But when I print them, I get different results:
ml = MyList('abc')
print(ml) # MyList(['a', 'b', 'c'])
ms = MySet('abc')
print(ms) # MySet(MySet({'c', 'a', 'b'}))
The display of MyList is what I would expect. But for some reason MySet is printed twice, as if the __repr__ is being called recursively or something. Does anyone know what's going on, or how best to get it to show MySet({'c', 'a', 'b'}), instead of MySet(MySet({'c', 'a', 'b'}))?
[Edit]
As of 3.9.5, in https://github.com/python/cpython/blob/main/Objects/setobject.c function set_repr has the code:
if (!PySet_CheckExact(so))
result = PyUnicode_FromFormat("%s({%U})",
Py_TYPE(so)->tp_name,
listrepr);
else
result = PyUnicode_FromFormat("{%U}", listrepr);
In other words, set is hardcoded so that its __repr__ omits the class name if the type is exactly a set and the set is non-empty, but shows the class name otherwise. But dict and list don't do this, for some reason.