3

I've found similar questions but those are quite old (6-8 years old) and might be outdated, and by the way, some replies lack explanations.

I have written the following piece of python-3 code in order to make a class which supports storing names and unnamed fields the same way you can have variable named and unnamed parameters in a function call :

class struct(list):
    def __init__(self, *args, **kwargs):
        super().__init__(args)
        for key, value in kwargs.items():
            setattr(self, key, value)

    def __repr__(self):
        s = super().__repr__()[1:-1]
        for key, val in self.__dict__.items():
            s += ', '+key+'='+repr(val)
        return 'struct('+s+')'

    def __str__(self):
        s = super().__str__()[1:-1]
        print('Debug : super().__str__() = "'+super().__str__()+'"')
        print('Debug : list(self).__str__() = "'+list(self).__str__()+'"')
        print('Debug : s = "'+s+'"')
        for key, val in self.__dict__.items():
            s += ', '+key+'='+str(val)
        return '{'+s+'}'

a = struct(1, 2, 3, a="akeja", b=21, c=True, d="lkj")
print('repr(a)="'+repr(a)+'"\n')
print('str(a)="'+str(a)+'"\n')

Executing this code in idle-3.5.2 will yield the following result :

>>> 
 RESTART: struct.py 
repr(a)="struct(1, 2, 3, b=21, d='lkj', a='akeja', c=True)"

Debug : super().__str__() = "struct(1, 2, 3, b=21, d='lkj', a='akeja', c=True)"
Debug : list(self).__str__() = "[1, 2, 3]"
Debug : s = "truct(1, 2, 3, b=21, d='lkj', a='akeja', c=True"
str(a)="{truct(1, 2, 3, b=21, d='lkj', a='akeja', c=True, b=21, d=lkj, a=akeja, c=True}"

>>> 

In this post, @Sven Marnach stressed out the need to use list.__init__(self, args) instead of super().__init__(args) in the __init__(...) method, but that doesn't change anything (in reality, he wrote list.__init__(self, *args), but that one doesn't work at all).

However, as one can see in the second debug lines, the call to super().__str__() which I expect to return the result from a call to the __str__ function from my super class (list), will in fact return something which looks very much like the expected return from self.__repr__()

I can't figure out why self.__repr__() seems to be called instead of list(self).__str__(). Should I directly call __str__() from list(self) like in @Sven Marnach 6y.o. advice ? Is that the right way to do ? Why ? Or is this just a bug, which I should report ?

Camion
  • 1,264
  • 9
  • 22
  • Looks like `list.__str__` calls `self.__repr__` for some reason. Strange. – Aran-Fey Nov 05 '18 at 14:50
  • 5
    `list.__str__()` does not exist, so Python falls back to the inherited `object.__str__()` - which invokes `__repr__()` on the original object, as a better option than the absolute last resort of "<*class* instance at 0x*hex*>". – jasonharper Nov 05 '18 at 14:53
  • @jasonharper : Then, why does it work, when I explicitly call `list(self).__str__()`, and why would it call `self.__repr__` instead of `self.__str__` (which would lead to infinite recursion - but how does it knows it is called from `self.__str__`) ? Shouldn't it preferably call `super().__repr__`, instead of `self.__repr__` ? shouldn't it anyway, be considered a bug ? – Camion Nov 05 '18 at 15:02
  • @Aran-Fey : Not when I call it explicitly – Camion Nov 05 '18 at 15:04
  • By the way, @jasonharper, calling `dir(list)` reports that there IS an `__str__` magic method in `list` and `callable(list.__str__)` returns `True`. – Camion Nov 05 '18 at 15:16
  • There is certainly a `__str__` *accessible* from `list`, but it's actually inherited from `object`. Try typing `list.__str__` in the interpreter, I get `` (that's Python 2.7, it may be worded a bit differently for you). – jasonharper Nov 05 '18 at 15:19
  • Ok, but then, how do you explain it calls my class `__repr__` method ? Isn't this wrong ? – Camion Nov 05 '18 at 15:23
  • Or else, is there another syntax to call my superclass canonically (I mean : not by it's name), in order to make python forget which is my current new class, the same way as when I call it by it's name (list) ? – Camion Nov 05 '18 at 15:29
  • @Camion: `list(self).__str__()` works because you explicitly converted to `list` with `list(self)`, it wasn't a `struct` at all after that point. Directly calling `list.__str__` on yourself would be `list.__str__(self)`, and would have the same issues as `super().__str__()`. – ShadowRanger Nov 05 '18 at 15:45
  • @ShadowRanger : Ok, but then, is there a right way to convert my class to it's superclass without naming it explicitely ? – Camion Nov 05 '18 at 23:35

0 Answers0