4

I was working on a game using the pygame library on Python. I basically defined a Character class from which the Knight class and Enemy class would inherit functions. Since both children classes use the same initialize functions, I defined the __init__() function under the parent class. However, I don't fully understand how it works and I'm getting the following error:

TypeError: __init__() takes 1 positional argument but 3 were given

Here's my code:

class Character():

    def __init__(self, img, hitbox, vel, pos_x, pos_y):
        self.img = img
        self.hitbox = hitbox
        self.vel = vel
        self.pos_x = pos_x
        self.pos_y = pos_y
    
    def draw(self):
        
        if self.right:
            pygame.transform.flip(self.img, True, False)
        
        win.blit(self.img, (self.pos_x, self.pos_y))

class Knight(Character):
    
    def __init__(self):
        Character.__init__(self)

    def move(self):
        if self.right:
            if self.x + self.vel < win_width:
                self.x += self.vel
        if self.left:
            if self.x - self.vel > 0:
                self.x -= self.vel

main_plr = Knight("img", (19, 20), 5, 30, 20)

2
  • 4
    You may as well just delete the __init__ out of the knight class since it doesnt do any of its own initializing Commented Oct 21, 2021 at 14:55
  • Read rhettinger.wordpress.com/2011/05/26/super-considered-super for advice on how to define __init__ in a way that avoids repetition and plays well with multiple inheritance. Commented Oct 21, 2021 at 15:05

2 Answers 2

4

For a quick fix: just remove the __init__ method from Knight.

The error is raised because you create a Knight object with 6 arguments (self, "img", (19, 20), 5, 30, 20) whereas the __init__ method accepts only one (self).

So if your Knight objects do not have any additional attributes compared to Character objects, it will be just fine to remove the __init__ method. Now if you want your knight to have weapons, for example, you will have to do something like that:

class Knight(Character):

   def __init__(self, img, hitbox, vel, pos_x, pos_y, weapon):
      super().__init__(img, hitbox, vel, pos_x, pos_y)
      self.weapon = weapon

k = Knight("img", (19, 20), 5, 30, 20, "sword")

[Edit]

Additionaly, as suggested by @Matiiss, you can use *args to avoid repeating all arguments of Character.__init__ in Knight.__init__. One advantage, besides conciseness, is that you do not have to modify Knight if you add attributes to your Character objects.

class Knight(Character):

    def __init__(self, *args, weapon):
        super().__init__(*args)
        self.weapon = weapon

k = Knight("img", (19, 20), 5, 30, 20, weapon="sword")

But now the drawback is that you have to specify the weapon with weapon="the-weapon", since it is now a keyword argument (placed after *args).

Sign up to request clarification or add additional context in comments.

5 Comments

additionally for shortening the code you could use def __init__(self, *args, weapon) and then super().__init__(*args), but then it would have to be instantiated like this: k = Knight('img', (19, 20), 5, 30, 20, weapon='sword')
@Matiiss Many thanks for the suggestion. I included it in my answer.
oh, actually you can do it like this: def __init__(self, *args): then super().__init__(args[:-1]) (or args[:5] which may be better) and then self.weapon = args[-1] (or args[5]), then it would be a positional argument (although not as explicit as what is should be (basically possible but explicitness is better so wouldn't really suggest using it))
@Matiiss args[:5] and args[5] are bad IMHO because then you lose one major advantage which is that you can modify Character.__init__ signature without having to modify Knight.__init__.
yes, that and also that it may be harder to understand what the argument represents
2

As the error you are seeing says, your Knight constructor does not accept those arguments; if you are going to use that kind of inherited method extension, the class and subclass methods need to have matching argument signatures. It's also best to use super() to refer to the superclass rather than naming it explicitly.

The simplest way of handling this is to use *args and **kwargs, to concisely pass arguments that aren't needed by the subclass method to the superclass method, ie

class Character():

    def __init__(self, img, hitbox, vel, pos_x, pos_y):
        self.img = img
        self.hitbox = hitbox
        self.vel = vel
        self.pos_x = pos_x
        self.pos_y = pos_y
    

class Knight(Character):
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def move(self):
        if self.right:
            if self.x + self.vel < win_width:
                self.x += self.vel
        if self.left:
            if self.x - self.vel > 0:
                self.x -= self.vel

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.