35

Being tired manually implementing a string representation for my classes, I was wondering if there is a pythonic way to do that automatically.

I would like to have an output that covers all the attributes of the class and the class name. Here is an example:

class Foo(object):
    attribute_1 = None
    attribute_2 = None
    def __init__(self, value_1, value_2):
         self.attribute_1 = value_1
         self.attribute_2 = value_2

Resulting in:

bar = Foo("baz", "ping")
print(str(bar)) # desired: Foo(attribute_1=baz, attribute_2=ping)

This question came to mind after using Project Lombok @ToString in some Java projects.

falsetru
  • 357,413
  • 63
  • 732
  • 636
Marco Ferrari
  • 4,914
  • 5
  • 33
  • 53
  • What Project Lombok does for Java? – Max Malysh Oct 02 '15 at 14:59
  • Boilerplate code reduction. Look here for features: https://projectlombok.org/features/index.html – Marco Ferrari Oct 02 '15 at 15:00
  • 1
    Actually, "boilerplate code reduction" means nothing. Lombok deals with specific Java problems. It is useless to search for "similar" tool, it's better to ask for something more concrete. – Max Malysh Oct 02 '15 at 15:07
  • 1
    Python *has* a default implementation for `__str__`, which forwards to `__repr__`. `__repr__` also has a default implementation, which mentions the results of `type(my_object)` and `id(my_object)`. If you want to use some other default, you could a) use inheritance, b) write your own class decorator, or c) assign `__str__` in the class body to some existing function (by doing `__str__ = something` rather than `def __str__(self)`). – lvc Oct 05 '15 at 09:38

3 Answers3

61

You can iterate instance attributes using vars, dir, ...:

def auto_str(cls):
    def __str__(self):
        return '%s(%s)' % (
            type(self).__name__,
            ', '.join('%s=%s' % item for item in vars(self).items())
        )
    cls.__str__ = __str__
    return cls

@auto_str
class Foo(object):
    def __init__(self, value_1, value_2):
        self.attribute_1 = value_1
        self.attribute_2 = value_2

Applied:

>>> str(Foo('bar', 'ping'))
'Foo(attribute_2=ping, attribute_1=bar)'
falsetru
  • 357,413
  • 63
  • 732
  • 636
4

You can use @dataclass, which automatically generates __init__(), __repr__(), __str__(), and more. You just need to add a @dataclass decorator to your class and add type annotations to the members. You can even remove your __init__() implementation then.

from dataclasses import dataclass

@dataclass
class Foo(object):
    attribute_1 : str
    attribute_2 : str

bar = Foo("baz", "ping")
print(str(bar)) # Prints: Foo(attribute_1='baz', attribute_2='ping')
sirain
  • 918
  • 10
  • 19
2

wrote this while falsetru answerred. Its the same idea, mine is very beginner friendly in terms of reading it, his is much nicer implemented imho

class stringMe(object):
        def __str__(self):
            attributes = dir(self)
            res = self.__class__.__name__ + "("
            first = True
            for attr in attributes:
                if attr.startswith("__") and attr.endswith("__"):
                    continue

                if(first):
                    first = False
                else:
                    res += ", "

                res += attr + " = " + str( getattr(self, attr))

            res += ")"
            return res

    class Foo(stringMe):
        attribute_1 = None
        attribute_2 = None
        def __init__(self, value_1, value_2):
             self.attribute_1 = value_1
             self.attribute_2 = value_2


bar = Foo("baz", "ping")
print(str(bar)) # desired: Foo(attribute_1=baz, attribute_2=ping)
Henrik
  • 2,180
  • 16
  • 29