In Python, is there a way to implement multiple representation strategies on custom classes, to provide different styles how they are shown, and the whole thing recursively on arbitrary data structures, so that nested objects would be represented using the parent call’s strategy, if available? Like this…
class SomeClass:
def __init__(self, objects):
self._objects = objects
def __repr__(self):
return f"Recursive default repr: {self._objects}"
def __alternate_repr__(self):
return f"Recursive alternate repr: {self._objects}"
a = SomeClass(dict(x=1))
b = SomeClass(["list", "of", "values"])
c = SomeClass([a, b, 4, "..."])
repr(c) # c,a,b shown using __repr__()
alternate_repr(c) # c,a,b shown using __alternate_repr__()
>Solution :
You’d probably want repr() to do the right thing (e.g. when called by other library code), and that code wouldn’t know about a __special_repr__ or whatever, so you’ll need some more roundabout plumbing here.
Using contextvars makes sure this is async/threading safe.
import contextlib
import contextvars
## Repr mode plumbing.
_repr_mode = contextvars.ContextVar("repr_mode", default="normal")
def get_repr_mode():
return _repr_mode.get()
@contextlib.contextmanager
def with_repr_mode(mode):
try:
token = _repr_mode.set(mode)
yield
finally:
_repr_mode.reset(token)
## Client classes using the repr mode.
class SomeClass(str):
def __repr__(self):
if get_repr_mode() == "special":
return f"SPECIAL ALL CAPS REPR! {self.upper()}"
return super().__repr__()
class SomeContainer(list):
def __repr__(self):
if get_repr_mode() == "special":
return f"SPECIAL REPR!: {super().__repr__()}"
return super().__repr__()
## Demo code.
lst = SomeContainer([SomeClass("hello"), SomeClass("world")])
print(repr(lst))
with with_repr_mode("special"):
print(repr(lst))
This prints out
['hello', 'world']
SPECIAL REPR!: [SPECIAL ALL CAPS REPR! HELLO, SPECIAL ALL CAPS REPR! WORLD]