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)
CodePudding user response:
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
CodePudding user response:
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
).