I've written a container type in Python and I'm trying to write a robust __repr__
method that correctly handles the case where the container contains itself.
For example, here's what the built-in list
does:
>>> x = []
>>> x.append(x)
>>> repr(x)
'[[...]]'
Container types written in C for CPython can achieve this functionality by using Py_ReprEnter
and Py_ReprLeave
. Is there equivalent functionality in pure-Python, or do I need to create my own?
Python __repr__() function returns the object representation in string format. This method is called when repr() function is invoked on the object. If possible, the string returned should be a valid Python expression that can be used to reconstruct the object again.
__repr__ (self) Returns a string as a representation of the object. Ideally, the representation should be information-rich and could be used to recreate an object with the same value.
Python also accepts function recursion, which means a defined function can call itself. Recursion is a common mathematical and programming concept. It means that a function calls itself. This has the benefit of meaning that you can loop through data to reach a result.
If you're using Python 3 you can make use of the reprlib.recursive_repr decorator.
You can create your own, but it's a bit of a pain if you want to do it properly: you shouldn't store a ‘being repr'd’ marker flag on the object itself because that's not thread-safe. Instead you can store a thread-local set of your instances that are being repr'd.
A much cheaper solution is to depend on a built-in repr
that takes care of recursion, eg.:
def __init__(self, *list):
self._list= list
def __repr__(self):
return 'mything('+repr(self._list)[1:-1]+')')
As long as one object in a recursion loop causes Py_ReprEnter
to happen, repr
can't form a complete loop.
How do I create a thread-local set of instances?
With the threading module:
class MyThing(object):
_local= threading.local()
_local.reprs= set()
def __repr__(self):
reprs= MyThing._local.reprs
sid= id(self)
if sid in reprs:
return 'MyThing(...)'
try:
reprs.add(sid)
return 'MyThing(%r)' % self.something
finally:
reprs.remove(sid)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With