-2

I've read through the answers here, and everyone seems in agreement that mapping str to a list should call element.__str__ on each element of that list, but __repr__ is being called instead. I've stepped through the code, and the list is being generated properly.

My Collection wrapper looks like this:

class Wrapper:
    def __init__(self, *args):
        self.foo = list(args)

    def __str__(self):
        return ", ".join(map(str, self.foo))

From Wrapper.__str__(), it jumps to __repr__ in the contained class. That class looks like this:

class MyType:
    def __init__(self, arg1, arg2, arg3):
        self.arg1 = arg1
        # set the other values

    def __repr__(self):
        return "I am a repr"

    def __str__(self):
        return "I am a str"

When I step through the code with a debugger, execution jumps directly from the map line in Wrapper to __repr__ in MyType. If I call str(wrapper_instance.foo[0]), I jump to __str__ instead. Why might this be?

EDIT: Wrapper is a superclass of the actual type that's being called, though the subclass doesn't implements a __str__. The subclass definition looks like this:

class SubWrapper(Wrapper):
    def __init__(self, definite_arg, *args):
        super().__init__(args)
        self.definite_arg = definite_arg

    def __repr__(self):
        return "I am a subclass repr"

In order to comply with the off-topic rule and to answer some of the questions about how the code is being called, the specific calls I'm making are:

>>> w = Wrapper(MyType(1, 2, 3), MyType(4, 5, 6))
>>> s = SubWrapper(MyType(7, 8, 9), MyType(1, 2, 3), MyType(4, 5, 6))
>>> print(str(w))
I am a str, I am a str
>>> print(str(s))
(I am a repr, I am a repr)
>>> print(", ".join(map(str, w.foo)))
I am a str, I am a str
>>> print(", ".join(map(str, s.foo)))
(I am a repr, I am a repr)

EDIT_2: It turns out that the issue was in the construction of the class - SubWrapper.__init__ should call super().__init__(*args)

Community
  • 1
  • 1
Nyefan
  • 251
  • 3
  • 10
  • 1
    `print(Wrapper(MyType(1,2,3), MyType(1,2,3)))` works exactly as expected. Are you instead using `print(Wrapper([MyType(1,2,3), MyType(1,2,3)]))` – donkopotamus Apr 12 '17 at 22:46
  • 3
    Cannot reproduce - if I fix the error in `MyType` (`self,` not `self.`) then do `print(Wrapper(MyType(1,2,3), MyType(4,5,6)))` I get `I am a str, I am a str`. I helps to have a running example and its output for us to compare. – tdelaney Apr 12 '17 at 22:48
  • @tdelaney and @donkopotamus, I added some more information to the question as well as the solution - I never dereferenced (I'm sure there's a python term for that, but I don't know it) `*args` when calling the constructor. Thank you for your help. – Nyefan Apr 13 '17 at 16:41
  • Ouch! Glad it worked out. I call that unpacking... but I'm not sure if that's official either. – tdelaney Apr 13 '17 at 16:53

1 Answers1

0

If you print a built-in container it will use the representation of each item 1:

>>> [MyType(), MyType()]
[I am a repr, I am a repr]

>>> print([MyType(), MyType()])
[I am a repr, I am a repr]

>>> Wrapper(MyType(), MyType()).foo
[I am a repr, I am a repr]

However if you map each element to a string then it will (obviously) use the string representation:

>>> ','.join(map(str, [MyType(), MyType()]))
'I am a str,I am a str'

>>> print(Wrapper(MyType(), MyType()))
I am a str, I am a str

So items inside a built-in container (like list) will be displayed with the __repr__ no matter if you use str or repr on the container. You explicitly need to convert each item so it's displayed using its __str__ method.


1 I removed the __init__ method from your MyType. This just makes the code snippets "shorter" and less convolved.

MSeifert
  • 145,886
  • 38
  • 333
  • 352