0

Suppose I had the following Player base class:

from abc import ABC, abstractmethod

class Player(ABC):

    def __init__(self, name, player_type):
        self.name = name
        self.player_type = player_type

Wizard:

from Player import Player

class Wizard(Player):

    def __init__(self, name, player_type = "Wizard"):
        super().__init__(self,name)

Main:

from Player import Player
from Wizard import Wizard


def main():

    gandalf = Wizard("Gandalf")
    print(gandalf.name)

    # Will print gandalf because the parameter assignment was shifted
    # because self was passed from the child to base class.
    print(gandalf.player_type)

if __name__ == "__main__":
    main()

I'm aware from this question, you shouldn't pass self from the subclass to the base class. That being said, suppose you made the mistake of doing so, the line print(gandalf.name) prints <Wizard.Wizard object at 0x049F20B0> because name was never assigned but what exactly does this value mean?

  • 1
    It means you assigned the `self` into `name`... – Stephen Rauch May 28 '18 at 19:41
  • @StephenRauch - Where does it come from? I understand why it prints that value, but how did come up with that value? –  May 28 '18 at 19:44
  • Your call to `super().__init__(self,name)` in `Wizard.__init__()` would fail because you need to pass `player_type` as well. – AGN Gazer May 28 '18 at 19:44
  • 3
    It's the string representation of the object itself: the result of `repr(gandalf)`. – chepner May 28 '18 at 19:45
  • @AGNGazer - The code will compile and run because I'm looking at it right now, however, if you attempt to print the `name` that value will be printed for the reason I specified above, but I want to understand where that value came from –  May 28 '18 at 19:45
  • That is the default string representation inherited from `object`. So try `print(gandalf)`. Realize, the `Wizard` object carries a reference to itself. – juanpa.arrivillaga May 28 '18 at 19:48
  • 2
    @AGNGazer this code would indeed work. The method expects three arguments and is being passed three arguments. The fact that two of them are `self` is irrelevant. – Daniel Roseman May 28 '18 at 19:49
  • @AGNGazer - You shouldn't pass `self` from the child class to the base class, doing so can throw the parameter assignments off. The cod will compile and run –  May 28 '18 at 19:49
  • 1
    @Sveta it is hard to understand your question. You are passing `self` in the argument for `name`, so that is the value that is assigned; what is confusing to you? – Daniel Roseman May 28 '18 at 19:50
  • @DanielRoseman - Right, it's only until you print the `name` that you realize there was an error. –  May 28 '18 at 19:50
  • 1
    Well... Going to eat my hat... You are right... I missed `self`! – AGN Gazer May 28 '18 at 19:50
  • @DanielRoseman - I want to know how that value was generated. What about the Python environment made it go, I'll assign that value when `self` is pass from child to base when it expected another parameter. –  May 28 '18 at 19:52
  • @chepner explained this in https://stackoverflow.com/questions/50572602/value-given-when-variable-is-empty#comment88154878_50572602. When you `print(gandalf.name)`, `name` is an object that is being converted to a string for the purpose of printing using `repr(name)`. – AGN Gazer May 28 '18 at 19:54
  • 1
    Note, the variable is not empty. Such a thing doesn't really exist in Python. – juanpa.arrivillaga May 28 '18 at 19:54

1 Answers1

2

super() does not return a class; it returns a proxy object, so super().__init__(self, name) behaves much the same as foo.__init__(self, name), where foo is a "real" instance of Wizard. That being the case, the first parameter self of the call to Player.__init__ has already been assigned, so your explicit self is assigned to the next parameter, name, and your second argument name is assigned to the third parameter player_type.

Put another way, with Player being the next class in the MRO, super().__init__(self, name) is the same as Player.__init__(self, self, name).

Put yet another way, super().__init__ is a bound method, and so the __init__ function's first parameter self has already been supplied.

chepner
  • 497,756
  • 71
  • 530
  • 681
  • So, I was correct in stating that it shifted the parameter assignments? –  May 28 '18 at 19:56
  • 1
    Yes, but that's normal for *any* bound method, just like `gandalf = Wizard("Gandalf")` results in an implicit call to `Wizard.__init__(gandalf, "Gandalf")` – chepner May 28 '18 at 19:58
  • In other words, don't pass `self`, that's done for you automatically, correct? –  May 28 '18 at 20:01
  • You can clean up the edit I made, but keep the comment about the `repr(gandalf)` because that's the real answer to my question, and I'll give you the checkmark. –  May 28 '18 at 20:07