Python __repr__ for all member variables
Asked Answered
P

4

14

Implementing __repr__ for a class Foo with member variables x and y, is there a way to automatically populate the string? Example that does not work:

class Foo(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def __repr__(self):
        return "Foo({})".format(**self.__dict__)

>>> foo = Foo(42, 66)
>>> print(foo)
IndexError: tuple index out of range

And another:

from pprint import pprint
class Foo(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def __repr__(self):
        return "Foo({})".format(pprint(self.__dict__))

>>> foo = Foo(42, 66)
>>> print(foo)
{'x': 42, 'y': 66}
Foo(None)

Yes I could define the method as

    def __repr__(self):
        return "Foo({x={}, y={}})".format(self.x, self.x)

but this gets tedious when there are many member variables.

Promising answered 16/6, 2017 at 17:50 Comment(0)
A
31

I use this as a mixin when I want something like that:

class SimpleRepr(object):
    """A mixin implementing a simple __repr__."""
    def __repr__(self):
        return "<{klass} @{id:x} {attrs}>".format(
            klass=self.__class__.__name__,
            id=id(self) & 0xFFFFFF,
            attrs=" ".join("{}={!r}".format(k, v) for k, v in self.__dict__.items()),
            )

It gives the class name, the (shortened) id, and all of the attributes.

Acapulco answered 16/6, 2017 at 17:55 Comment(6)
Nice one! Real classy.Bascio
Why isn’t it the default object.__repr__ implementation?Adlay
Note that one should probably use type(self).__name__ instead of self.__class__.__name__ to avoid returning virtual class names for proxy objects (cf. this answer).Adlay
@Maggyero: If you're writing a proxy object, you probably shouldn't use this mixin for it at all. It'll give misleading results no matter which class name you pick.Pratt
(I'd recommend using the full ID instead of shortening it - people will make assumptions about what the number after that @ sign means, and I think the shorter output isn't worth the confusion caused by shortening the ID.)Pratt
@Maggyero it's not the default it could lead to infinite recursion if there's circular references. See https://mcmap.net/q/25308/-what-is-the-difference-between-__str__-and-__repr__Halfhearted
S
10

I think you want something like this:

    def __repr__(self):
        return "Foo({!r})".format(self.__dict__)

This will add repr(self.__dict__) in the string, using !r in a format specifier tells format() to call the item's __repr__().

See the "Conversion field" here: https://docs.python.org/3/library/string.html#format-string-syntax


Based on Ned Batchelder's answer, you can replace the line above by

return "{}({!r})".format(self.__class__.__name__, self.__dict__)

for a more generic approach.

Sportsmanship answered 16/6, 2017 at 17:54 Comment(1)
If self refers to itself, such an implementation will lead to an infinite loop such that you will have to filter self.__dict__ first.Albritton
E
0

Nice example!

for pretty output better to place simple return "\n{!r}".format(self.__dict__) and in root full print return "Class name: '{}' \n{!r}".format(self.__class__.__name__, self.__dict__)

Ezra answered 13/6, 2018 at 10:55 Comment(0)
E
0

Generally __repr__ means that you can copy-paste the result and it can easily be used again for creating a new object. Therefore, the following may look a bit hacky but will do the job. Also, notice how we need to treat string type arguments differently from numeric types.

class Foo:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        repr_str = f"{self.__class__.__name__}"
        repr_str += '('
        
        for key, val in self.__dict__.items():
            val       = f"'{val}'" if isinstance(val, str) else val
            repr_str += f"{key}={val}, "
        
        return repr_str.strip(", ") + ')'


>>> foo = Foo(42, 66)
>>> print(foo)
Foo(x=42, y=66)

>>> foo2 = Foo("abc", "xyz")
>>> print(foo2)
Foo(x='abc', y='xyz')
Empirin answered 11/8, 2023 at 9:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.