2

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)
kaya3
  • 47,440
  • 4
  • 68
  • 97
Jimbo
  • 2,886
  • 2
  • 29
  • 45

2 Answers2

2

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
kaya3
  • 47,440
  • 4
  • 68
  • 97
  • 1
    Interesting approach with the unary plus operator! It reminds me of the '?' help operator in R. – Jimbo Feb 04 '20 at 19:42
2

__repr__ is intended to provide a short, often programmatic display of an object. It's used as the method of choice for all built in containers to display elements. You should therefore override __repr__ to provide your short output.

__str__ is the function intended for the full fancy display of an object. It's what normally shows up when you print an object. You can also trigger it by calling str. You should put your long fancy output in __str__, not __repr__.

The only modification you will have to make is to explicitly call print(obj) or str(obj) rather than repr(obj) or just obj in the REPL. As @kaya3's excellent answer shows, stack inspection won't help you much, and in my opinion would not be a clean solution even if it did.

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264