0

I would like to have a class

class Test:
    def __repr__(self):
        if self in a list/set:
            return self.__class__.__name__
        else:
            return 'My pretty print like Numpy/Tensorflow'

that is able to have an "adaptive" __repr__(). When I simply print the instance, it should print the pretty print.

a = Test()
a
'My pretty print like Tensorflow/numpy'

When I put the object in a list and print the list, it just shows the object name.

b = Test()
z = [a, b]
z
[a, b]

I need this feature because normally printing z will give ['My pretty print like Tensorflow/numpy', 'My pretty print like Tensorflow/numpy'] which looks ugly.


My attempt: use garbage collector to know if the object is in a list?

null
  • 1,167
  • 1
  • 12
  • 30

3 Answers3

1

You can't. You can't reliably detect if your object is referenced by a container object and that your __repr__ is being called to produce the __repr__ output of that container.

That's because your object can be referenced by any number of other objects. The container object might have an internal data structure that holds the reference, the __repr__ implementation will have a temporary reference, your object could be referenced by multiple containers, etc. Next, detecting that you are actually being called as part of a __repr__ call for the parent container could easily fail because your __repr__ value could be cached, or could be part of some intermediate object that itself is only created as a utility object to produce that output.

Libraries like numpy and tensorflow instead have their own container formats where the special output they produce is part of that container, and not part of the classes whose instances are contained.

If you want to produce 'nice' human readable output, define __str__ and use print(yourobject). __repr__ is there to produce debug-friendly output.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
1

Instead of messing around with garbage collector, just make a PrettyList subclass that does this and use that:

class PrettyList(list):
    def __str__(self):
        return str([str(x) for x in self])
Ryan Laursen
  • 617
  • 4
  • 18
-1

You can use the traceback module to get the instance's variable name, and you can use the inspect module to find out the stack trace of the __repr__ call. That way, you can figure out if it was a list which ended up calling the representation, or just by calling __repr__ directly.

If you put it all together, you get something like this:

import traceback
import inspect

# Class definition
class Test:

    # Initialization
    def __init__(self):
        # https://stackoverflow.com/a/1690751/11985743
        (filename,line_number, function_name,text) = traceback.extract_stack()[-2]
        # Get the variable name
        self.defined_name = text[:text.find('=')].strip()

    # Representation
    def __repr__(self):
        # For each stack call
        for stack_call in inspect.stack():
            # If the code that called it is the same code to represent lists
            if stack_call.code_context:
                if stack_call.code_context[0].strip() == "p.pretty(x)":
                    # Then it was represented in a list, so return the defined name
                    return self.defined_name
        # Otherwise, it was represented directly, so return the proper representation
        return 'My pretty print like Numpy/Tensorflow'

And if you try running a test case:

In : z = Test()

In : z
Out: My pretty print like Numpy/Tensorflow

In : [z]
Out: [z]
Xiddoc
  • 3,369
  • 3
  • 11
  • 37
  • `traceback` is a higher-level user of `sys._getframe()`. You **can't rely on this**, there is no guarantee that the variable you find actually references your object and how this is connected to a `__repr__` call that outputs a string containing your object `__repr__`. – Martijn Pieters Feb 26 '21 at 15:48
  • And there is no guarantee of an assignment statement _either_, e.g. `globals().update({"foo": Test()})` would create or replace the global name `foo` with a new instance of `Test()` assigned to it. – Martijn Pieters Feb 26 '21 at 15:50