I'm trying to implement __repr__ for a class, but when I want to access function members to add there value to __repr__ I get "maximum recursion depth exceeded".
I've noticed that if I remove all the class' functions the __repr__ works as expected.
I intend to do a workaround to skip the members which are function, but I want to understand why the recursion is happening.
Se below a simplified code.
class C:
def __init__(self, a):
self.a = a
def getA(self):
return self.a
def __repr__(self):
ret_str = self.__class__.__name__ + "\n"
for v in dir(self):
if v.startswith("__"):
continue
ret_str += "{} -> {}\n".format(v, getattr(self,v))
return ret_str
c = C(1)
print(c)
here is the error
Traceback (most recent call last):
File "test.py", line 18, in <module>
print(c)
File "test.py", line 13, in __repr__
ret_str += "{} -> {}\n".format(v, getattr(self,v))
File "test.py", line 13, in __repr__
ret_str += "{} -> {}\n".format(v, getattr(self,v))
File "test.py", line 13, in __repr__
ret_str += "{} -> {}\n".format(v, getattr(self,v))
[Previous line repeated 163 more times]
RecursionError: maximum recursion depth exceeded
Without your custom __repr__
, formatting c.getA
(i.e. what ret_str += "{} -> {}\n".format(v, getattr(self,v))
does when v
is self.getA
) results in
<bound method C.getA of <__main__.C object at 0x1101f05c0>>
IOW, the default __repr__
of a bound method requires repr
ing the object it is bound to, resulting in unbound recursion. If your __repr__
had limited recursion, the result would be something like
C
getA -> <bound method C.getA of C
getA -> <bound method C.getA of C
getA -> <bound method C.getA of C
getA -> <bound method C.getA of C
...
which is likely not what you want anyway.
You could add a guard that prevents this special behavior from happening on recursive calls:
class C:
def __init__(self, a):
self.a = a
def getA(self):
return self.a
def __repr__(self):
try:
if getattr(self, "_being_repred", False):
# Recursive call to __repr__, don't do special things
return super().__repr__()
self._being_repred = True
ret_str = self.__class__.__name__ + "\n"
for v in dir(self):
if v.startswith("__"):
continue
ret_str += "{} -> {}\n".format(v, getattr(self, v))
return ret_str
finally:
self._being_repred = False
c = C(1)
print(c)
The output is
C
getA -> <bound method C.getA of <__main__.C object at 0x1268c0890>>
but that also gets weird if the "root call" wasn't directly to repr(c)
, e.g. print(c.getA)
outputs
<bound method C.getA of C
getA -> <bound method C.getA of <__main__.C object at 0x1268c0800>>
>
EDIT: The fact bound methods' reprs repr the bound object stems from this code from 2001 (released in Python 2.2), complete with a /* XXX Shouldn't use repr() here! */
comment. The current version of that code still retains the same comment, but there's no longer a fallback behavior when repr
fails.