Python: trouble inheriting from multiple parent classes - python

I have a subclass that needs to inherit from two parents differing only in methods and a single property. When instantiating this class, I'm getting an error stating I'm using to many parameters. When removing a param it says I'm not using enough.
Ball
class Ball:
"""
base class for bouncing objects
"""
def __init__(self, bounds, position, velocity, color, radius):
self.position = position
self.velocity = velocity
self.bounds = bounds
self.color = color
self.radius = radius
def update(self):
# bounce at edges. TODO: Fix sticky edges
if self.position.x < 0 + self.radius or self.position.x > self.bounds[0] - self.radius: # screen width
self.velocity.x *= -1
if self.position.y < 0 + self.radius or self.position.y > self.bounds[1] - self.radius: # screen height
self.velocity.y *= -1
self.position += self.velocity
def draw(self, screen, pygame):
# cast x and y to int for drawing
pygame.draw.circle(screen, self.color, [int(self.position.x), int(self.position.y)], self.radius)
BouncingBall
class BouncingBall(Ball):
"""
ball effected by gravity
"""
def __init__(self, bounds, position, velocity, color, radius, weight):
super().__init__(bounds, position, velocity, color, radius)
self.weight = weight
def update(self):
KineticBall
class KineticBall(Ball):
"""
A ball that collides with other collidable balls using simple elastic circle collision
"""
def __init__(self, bounds, position, velocity, color, radius, object_list):
super().__init__(bounds, position, velocity, color, radius)
self.object_list = object_list
KineticBouncing
class KineticBouncing(BouncingBall, KineticBall):
def __init__(self, bounds, position, velocity, color, radius, weight, object_list):
super().__init__(bounds, position, velocity, color, radius, weight, object_list)
ball => KineticBouncing
# super().__init__(bounds, position, velocity, color, radius, weight, object_list)
TypeError: __init__() takes 7 positional arguments but 8 were given
ball = KineticBouncing(SCREEN_SIZE, Vector2(50, 50), Vector2(3, 3), [255, 0, 255], 10, -1, object_list)
Lets try something else...
So this is confusing.. instead I find Python3 Multiple Inheritance which I'm sure will solve my problem. Just use the parents name + init instead of super() right?
KineticBouncing
class KineticBouncing(BouncingBall, KineticBall):
def __init__(self, bounds, position, velocity, color, radius, weight, object_list):
BouncingBall.__init__(self, bounds, position, velocity, color, radius, weight)
KineticBall.__init__(self, bounds, position, velocity, color, radius, object_list)
ball => KinetBouncing
#Traceback (most recent call last):
# File "draw.py", line 99, in <module>
# main()
# File "draw.py", line 61, in main
# debug_create_balls(object_list)
# File "draw.py", line 43, in debug_create_balls
# ball = KineticBouncing(SCREEN_SIZE, Vector2(50, 50), Vector2(3, 3), [255, 0, 255], 10, -1, object_list)
# File "/home/adam/Desktop/web_dev/lambda_school/python/Python-OOP-Toy/src/ball.py", line 115, in __init__
# BouncingBall.__init__(self, bounds, position, velocity, color, radius, weight)
# File "/home/adam/Desktop/web_dev/lambda_school/python/Python-OOP-Toy/src/ball.py", line 33, in __init__
# super().__init__(bounds, position, velocity, color, radius)
#TypeError: __init__() missing 1 required positional argument: 'object_list'
ball = KineticBouncing(SCREEN_SIZE, Vector2(50, 50), Vector2(3, 3), [255, 0, 255], 10, -1, object_list)
So how in the world am I supposed to inherit from these two parent classes?

As others have pointed out, you should redesign your use of classes a bit. The immediate problem is that super() resolves to the first parent class of the object in question (self), rather than the parent class of the __init__ method you're in at the time.
When you try to initialize your KineticBouncing object, you invoke KineticBouncing.super().__init__(). This explicitly calls the __init__ methods of its two parent classes. When it first calls the one in BouncingBall, the first active statement is
super().__init__(bounds, position, velocity, color, radius)
I believe that you expect this to call Ball.__init__; that's not how super works. Instead, it resolves super based on the object, which is of class KineticBouncing. So ... what is super to KineticBouncing? Note that between the two __init__ calls as you've written them, you have to be wrong at least once.
I'll leave you to read the links provided in the comments. These will help you think in terms of Python's inheritance structure. With what you've posted, I think you'll have little trouble handling the switch; you merely picked a hierarchy model from elsewhere.

Use *args in all your parent's init
class Ball(object):
def __init__(self, *args):
print ('IN Ball')
class BouncingBall(Ball):
"""
ball effected by gravity
"""
def __init__(self, bounds, position, velocity, color, radius, weight, *args):
print ('IN BouncingBall')
super().__init__(bounds, position, velocity, color, radius, *args)
self.weight = weight
class KineticBall(Ball):
"""
A ball that collides with other collidable balls using simple elastic circle collision
"""
def __init__(self, bounds, position, velocity, color, radius, object_list, *args):
print ('IN KineticBall')
super().__init__(bounds, position, velocity, color, radius, *args)
self.object_list = object_list
class KineticBouncing(BouncingBall, KineticBall):
def __init__(self, bounds, position, velocity, color, radius, weight, object_list):
print ('IN KineticBouncing')
super().__init__(bounds, position, velocity, color, radius, weight, object_list)
Now on creating a new kinetic bouncing ball
ball = KineticBouncing('SCREEN_SIZE', 'Vector2(50, 50)', 'Vector2(3, 3)', [255, 0, 255], 10, -1, 'object_list')
IN KineticBouncing
IN BouncingBall
IN KineticBall
IN Ball

Related

Pygame Sprite Drawing on Surface issue

The problem that I am encountering is that my source object is not a surface, and the problem is not in my code, but in the code that designed the sprites for pygame, and I don't know how to fix it. I'll put the code that I made in there anyway, in case it's the problem.
Error Message:
Traceback (most recent call last):
File "C:\Users\Daniel\Desktop\Thing.py", line 22, in <module>
level.run()
File "C:\Users\Daniel\Desktop\level2.py", line 63, in run
self.crate_sprites.draw(self.display_surface)
File "C:\Users\Daniel\AppData\Local\Programs\Python\Python310\lib\site-
packages\pygame\sprite.py", line 551, in draw
self.spritedict.update(zip(sprites, surface.blits((spr.surface, spr.rect)
for spr in sprites)))
TypeError: Source objects must be a surface
Code in sprites.py:
def draw(self, surface):
"""draw all sprites onto the surface
Group.draw(surface): return Rect_list
Draws all of the member sprites onto the given surface.
"""
sprites = self.sprites()
if hasattr(surface, "blits"):
self.spritedict.update(zip(sprites, surface.blits((spr.surface,
spr.rect) for spr in sprites)))
else:
for spr in sprites:
self.spritedict[spr] = surface.blit(spr.image, spr.rect)
self.lostsprites = []
dirty = self.lostsprites
Tiles:
class StaticTile(Tile):
def __init__(self, size, x, y, pos, surface):
super().__init__(size, x, y, (x,y))
self.image = surface
self.surface = surface
class Crate(StaticTile):
def __init__(self, size, x, y, pos, surface):
super().__init__(size, x, y,
pygame.image.load('C:\\desktop\\game\\crate.png').convert_alpha(), (x,
y))
Layout:
crate_layout = import_csv_layout(level_data['crates'])
self.crate_sprites = self.create_tile_group(crate_layout, 'crates')
Draw/Update:
self.crate_sprites.update(self.world_shift)
self.crate_sprites.draw(self.display_surface)
The signature of the constructor of StaticTile is:
def __init__(self, size, x, y, pos, surface):
So it needs to be called like this (in Crate. __init__)
super().__init__(size, x, y, pygame.image.load('C:\\desktop\\game\\crate.png').convert_alpha(), (x, y))
super().__init__(size, x, y, (x, y),
pygame.image.load('C:\\desktop\\game\\crate.png').convert_alpha())

The particle is not moving so what is the problem?

I follow a youtube video 'https://www.youtube.com/watch?v=cCiXqK9c18g' and in the video he make a class that represent a ball and he used pymunk to make a body and added it to the space and after that he created a method inside the ball class that will use pygame to draw the ball and I did almost like him
import pygame
import pymunk
pygame.init()
fps = 60
dt = 1/fps
dsX = 800 # screen width
dsY = 500 # screen height
display = pygame.display.set_mode((dsX, dsY))
space = pymunk.Space()
clock = pygame.time.Clock()
def convert_cor(point): # convet the coordinates from pymunk to pygame coordinates
return point[0], dsY - point[1]
class Particle: # v: velocity, pos: position[x, y], r: radius of particle(Circle)
def __init__(self, pos = [0, 0], v = [0, 0], r = 10, color = (255, 0, 0)):
self.pos = pos
self.v = v
self.r = r
self.color = color
self.body = pymunk.Body()
self.body.position = self.pos
self.body.velocity = self.v # this is the veclocity
self.shape = pymunk.Circle(self.body, self.r)
self.shape.dencity = 1
self.shape.elasticity = 1
space.add(self.body, self.shape)
def draw(self):
pygame.draw.circle(display, self.color, convert_cor(self.pos), self.r)
class Box: # thickness of the sides of the box and L1, L2, L3, L4 are the sides of the box
def __init__(self, thickness, color):
self.thickness = thickness
self.color = color
L1 = pymunk.Body(body_type = pymunk.Body.STATIC)
L2 = pymunk.Body(body_type = pymunk.Body.STATIC)
L3 = pymunk.Body(body_type = pymunk.Body.STATIC)
L4 = pymunk.Body(body_type = pymunk.Body.STATIC)
L1_shape = pymunk.Segment(L1, (0, 0), (dsX, 0), self.thickness)
L2_shape = pymunk.Segment(L2, (dsX, 0), (dsX, dsY), self.thickness)
L3_shape = pymunk.Segment(L3, (dsX, dsY), (0, dsY), self.thickness)
L4_shape = pymunk.Segment(L4, (0, dsY), (0, 0), self.thickness)
space.add(L1, L1_shape)
space.add(L2, L2_shape)
space.add(L3, L3_shape)
space.add(L4, L4_shape)
def draw(self):
pygame.draw.line(display, self.color, convert_cor((0, 0)), convert_cor((dsX, 0)), self.thickness * 2)
pygame.draw.line(display, self.color, convert_cor((dsX, 0)), convert_cor((dsX, dsY)), self.thickness * 2)
pygame.draw.line(display, self.color, convert_cor((dsX, dsY)), convert_cor((0, dsY)), self.thickness * 2)
pygame.draw.line(display, self.color, convert_cor((0, dsY)), convert_cor((0, 0)), self.thickness * 2)
def Sim(): # the infinite while loop as a function
box = Box(2, (0, 255, 255))
particle = Particle(pos =[dsX/2, dsY/2], v = [-200, 500]) # here i gave the position and the velocity
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
display.fill((255, 255, 255))
box.draw()
particle.draw()
clock.tick(fps)
space.step(dt)
pygame.display.update()
Sim()
pygame.quit()
The thing is, I did also a class that will add a rigid sides for the display and i drew the sides from the Box class using the method 'draw' The problem is in the time 5:58 in the video he gave the ball velocity and it start moving and in my code it does not move. any idea why it doen't move?
note: I called the ball particle in my code
You error is both a typo and using the wrong variable.
Inside your particles draw function...
# OLD
def draw(self):
pygame.draw.circle(display, self.color, convert_cor(self.pos), self.r)
# New
def draw(self):
pygame.draw.circle(display, self.color, convert_cor(self.body.position), self.r)
You have to use the body's position cause that is the position of the physics body in pymunk's space.
Secondly...
class Particle: # v: velocity, pos: position[x, y], r: radius of particle(Circle)
def __init__(self, pos, v, r=10, color=(255, 0, 0)):
...
# Old
self.shape.dencity = 1
# New
self.shape.density = 1
Since density was not set to anything Pymunk was having a divide by zero error so it wouldn't update the body's position.
self.body.position = self.pos
To be clear about the problem:
From what I could find in the documentation, pymunk.Body.position is a property; it expects you to pass either an ordinary Python tuple or a Vec2d (not a list, although anything it's relying on internally to handle a tuple probably handles a list just fine), and calls some internal code written in another programming language. The effect is that, instead of storing your list object and then making changes to the list (which would be visible in your class, since it would be the same object), it just gets the initial values out of your list, and then doesn't use the list object.
This means that when Pymunk applies physics calculations, self.body.position changes, but self.pos does not. So the current self.pos is useless; we can't use it to check the object position.
If you don't need to do that, then there is no need to create a self.pos at all - just feed self.body.position = pos directly, and make sure to use self.body.position when drawing.
If you do, I recommend using your own property instead of trying to set self.pos. Do the above, and then add to the class:
#property
def pos(self):
return self.body.position
And if you want to change the position from your code (but you probably shouldn't! Why else are you using the physics engine in the first place?), also add:
#pos.setter
def pos(self, value):
self.body.position = value

Too many posit. arguments and no value for argument python OOP

Hi Im just starting to learn OOP and I use python to do so. Recently I ve been trying to code a game. I ve declared class Character that should be general and from that class my other classes will inherit. Now Im trying to create a class Player, I want to inherit everything but 1 variable. Here is a code:
class Character:
def __init__(self, x, y, width, height):
self.width = width
self.height = width
self.x = x
self.y = y
self.vel = 3
self.right = False
self.left = False
self.walk_count = 0
self.is_jump = False
self.jump_count = 10
self.standing = True
class Player(Character):
def __init__(self, x, y, width, height, right, left, walk_count, is_jump, jump_count, standing):
super().__init__(x, y, width, height, right, left, walk_count, is_jump, jump_count, standing)
self.vel = 5
in super() I keep getting pylint error:
Too many positional arguments for method call.
But I think it lets me run it even with it.
Also I want to put in only self.x, self.y, self.width and self.height when initializing, should I declare these values in class Player or let it be in class Character?
Then here I try to create an instance of class Player:
man = Player(200,410,64,64)
And I got errors missing value for argument right, left, walk_count,
is_jump, jump_count, standing
I tought that if i set values for them in class Character that I dont need to put values in when creating instance, because I want to set them to default values at the creation and then I will change them if I need it.
Later I will add class Enemy that will inherit most of the parameters from class Character also.
Then, you must call the super method with the signature (number of parameters) desired.
Change
def __init__(self, x, y, width, height, right, left, walk_count, is_jump, jump_count, standing):
super().__init__(x, y, width, height, right, left, walk_count, is_jump, jump_count, standing)
self.vel = 5
to
def __init__(self, x, y, width, height):
super().__init__(x, y, width, height)
self.vel = 5
Or, if you prefer, change Character's __init__ to match the Player's super call, and change:
class Character:
def __init__(self, x, y, width, height):
to
class Character:
def __init__(self, x, y, width, height, right, left, walk_count, is_jump, jump_count, standing):
and leave the super call as is.
The best approach is to only pass what is needed. If the only needed values to parameterize your Character object are x,y,height,weight, then you should only pass them - the remaining will be set automatically (you don't need to give them as input, since they will be discarded).
So, the correct approach is the first I mentioned.

(Python) Running a function for all objects in a list

I have a class called Wall, with a function called draw, which draws a line on a pygame screen. i would like to be able run the draw function for all wall objects without having to go back to the draw section.
heres the code for the class
class Wall():
def __init__(self, Pos1, Pos2):
self.name = self
self.Pos1 = Pos1
self.Pos2 = Pos2
print("wall initalized")
def draw(self):
pygame.draw.line(screen, white, self.Pos1, self.Pos2, 3)
pygame.draw.circle(screen, white, self.Pos1, 5)
pygame.draw.circle(screen, white, self.Pos2, 5)
print("wall drawn")
If you have a list of all the wall objects you can do the following:
for wall in list_of_walls:
wall.draw()

super() method initializes only some parameters

while programming a minigame, I stumbled across something I cant explain myself (fairly new to python).
This is my code:
class Block:
def __init__(self, x, y, hitpoints=1, color=(255, 0, 0), width=75, height=35):
self.color = color
self.x = x
self.y = y
self.height = height
self.width = width
self.hitpoints = hitpoints
class Ball(Block):
def __init__(self, x, y, size, color=(255, 255, 255), velocity=1):
super().__init__(self, x, y, color)
self.velocity = velocity
self.size = size
I initialize the object ball with
ball = Ball(x=200, y=200, size=30)
Problem arises when I call ball.x, as it returns
<Objects.Ball object at 0x00000249425A3508>.
If i call ball.y it works as intended and returns 200.
I can fix the whole problem by modifying the class Ball as follows:
class Ball(Block):
def __init__(self,x, y, size, color=(255, 255, 255), velocity=1):
super().__init__(self, y, color)
self.velocity = velocity
self.size = size
self.x = x
Can somebody explain to me why this happens?
Thanks alot!
You need to call super without self argument:
super().__init__(x, y, color=color)
This PEP explains how this works:
The new syntax:
super()
is equivalent to:
super(__class__, <firstarg>)
where __class__ is the class that the method was defined in, and
is the first parameter of the method (normally self for
instance methods, and cls for class methods).

Categories