2

Below is the definition for a simple Python class.

class Student:
    def __init__(self, name, grade):
        self.name = name
        self.grade = grade

    def __str__(self):
        return self.name

As you see, we override the __str__ method to return the student's name. If, for example, I declare a variable x = Student("Trevor", 7) and subsequently call print(x), I see the output Trevor in the console. Everything is good.

But, if I execute the snippet below, what happens is so dramatically unexpected that it nearly convinced me we're living in a simulation and nothing is real.

s1 = Student("James", 11)
s2 = Student("Charlie", 8)
s3 = Student("Alice", 9)
s4 = Student("Dana", 12)
students = [s1, s2, s3, s4]
print(students)

My Java brain expects the output to be [James, Charlie, Alice, Dana], but what I see is [<__main__.Student object at 0x00A7E6D0>, <__main__.Student object at 0x00AFB310>, <__main__.Student object at 0x00AFBB68>, <__main__.Student object at 0x00AFBAD8>].

How is this possible? How is anything possible? Am I real? Are you real? I just don't get it. How? Why? WHY?

  • Maybe the list's `__str__` method does not call `__str__` on it's elements. However, you can do it manually, and it will work. – nagyl Mar 31 '21 at 18:16
  • 1
    The `__str__()` of most container types, such as lists, is built from the `__repr__()` of its elements, not their `__str__()`. You could put `__repr__ = __str__` at the bottom of your class definition to use the same method for both. – jasonharper Mar 31 '21 at 18:17
  • ...or just define `__repr__` instead. `__str__` will fallback to it if not defined... – Tomerikoo Mar 31 '21 at 18:35

1 Answers1

6

Your assumption that list recursively calls str on its elements is wrong. list calls repr, which is implemented through the magic method __repr__:

>>> class Foo:
...   def __init__(self, n):
...       self.n = n
...   def __repr__(self):
...       return self.n
...
>>> [Foo('a'), Foo('b')]
[a, b]
>>> print(_)
[a, b]

However, the goal of the __repr__ method is not to give a plain string back as in this case, but a representation that can be used to recreate the object.

MatsLindh
  • 49,529
  • 4
  • 53
  • 84
  • 1
    You might want to mention that by default, str uses repr, but not the other way around – Mad Physicist Mar 31 '21 at 18:22
  • Very interesting. This actually makes a lot of sense. Realistically, you would only print a list directly for debugging purposes. So, it's a better idea to invoke `__repr__` on the elements instead of `__str__`. Also, python lists are not typed, making the "official" string representation of an element that much more relevant. – ajstylesfan2002 Mar 31 '21 at 19:09