I have a (bad?) habit of displaying classes in Python like structures in Matlab, where each attribute is printed along with its value in a nice clean layout. This is done by implementing the __repr__
method in the class.
When working with objects inside of dictionaries or lists, this display style can be a bit distracting. In this case I'd like to do a more basic display.
Here's the envisioned pseudocode:
def __repr__(self):
if direct_call():
return do_complicated_printing(self)
else:
#something simple that isn't a ton of lines/characters
return type(self)
In this code direct_call()
means that this isn't being called as part of another display call. Perhaps this might entail looking for repr
in the stack? How would I implement direct call detection?
So I might have something like:
>>> data
<class my_class> with properties:
a: 1
cheese: 2
test: 'no testing'
But in a list I'd want a display like:
>>> data2 = [data, data, data, data]
>>> data2
[<class 'my_class'>,<class 'my_class',<class 'my_class'>,<class 'my_class'>]
I know it is possible for me to force this type of display by calling some function that does this, but I want my_class
to be able to control this behavior, without extra work from the user in asking for it.
In other words, this is not a solution:
>>> print_like_I_want(data2)
This is a strange thing to want to do, and generally a function or method ought to do the same thing whoever is calling it. But in this case, __repr__
is only meant for the programmer's convenience, so convenience seems like a good enough reason to make it work the way you're asking for.
However, unfortunately what you want isn't actually possible, because for whatever reason, the list.__repr__
method isn't visible on the stack. I tested in Python 3.5.2 and Python 3.8.1:
>>> class ReprRaises:
... def __repr__(self):
... raise Exception()
...
>>> r = ReprRaises()
>>> r
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __repr__
Exception
>>> [r]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __repr__
Exception
As you can see, the stack is the same whether or not the object being repr
'd is in a list. (The __repr__
frame on the stack belongs to the ReprRaises
class, not list
.)
I also tested using inspect.stack
:
>>> import inspect
>>> class ReprPrints:
... def __repr__(self):
... print(*inspect.stack(), sep='\n')
... return 'foo'
>>> r = ReprPrints()
>>> r
FrameInfo(frame=<frame object at 0x7fcbe4a38588>, filename='<stdin>', lineno=3, function='__repr__', code_context=None, index=None)
FrameInfo(frame=<frame object at 0x7fcbe44fb388>, filename='<stdin>', lineno=1, function='<module>', code_context=None, index=None)
foo
>>> [r]
FrameInfo(frame=<frame object at 0x7fcbe4a38588>, filename='<stdin>', lineno=3, function='__repr__', code_context=None, index=None)
FrameInfo(frame=<frame object at 0x7fcbe44fb388>, filename='<stdin>', lineno=1, function='<module>', code_context=None, index=None)
[foo]
Again, there's no visible difference in the call stack between the object itself vs. the object in a list; so there's nothing for your __repr__
to check for.
So, the closest you can get is some kind of print_like_I_want
function. This can at least be written in a way that lets each class define its own behaviour:
def pp(obj):
try:
_pp = obj._pp
except AttributeError:
print(repr(obj))
else:
print(_pp())
The only way I can think of to do it with fewer keypresses is by overloading a unary operator, like the usually-useless unary plus:
>>> class OverloadUnaryPlus:
... def __repr__(self):
... return 'foo'
... def __pos__(self):
... print('bar')
...
>>> obj = OverloadUnaryPlus()
>>> obj
foo
>>> +obj
bar