super() method initializes only some parameters - python

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).

Related

Check if variable is defined in the superclass or subclass

This is just academic. Consider the following example:
class Shape():
def __init__(self, x, y):
self.x = x
self.y = y
def position(self):
return self.x, self.y
class Rectangle(Shape):
def __init__(self, x, y, height, width):
#super().__init__(x, y) # OK
self.x = x # not OK!
self.y = y # not OK!
self.height = height
self.width = width
r1 = Rectangle(1, 2, 3, 4)
If I asked someone to implement the class Rectangle, and they implemented with the "not OK" statements (instead of the with the "OK" statement as they should), then r1.x would be an attribute of Rectangle instead of Shape. Is there a way I can check if r1.x is a member of the Rectangle class or of the Shape class? Essentially, I want to make sure the initialization of the super class is being run, and no unnecessary new attributes are being created.

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: trouble inheriting from multiple parent classes

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

Python: Calling class method instead of parent constructor

Say I have the following class definition:
class WorldObject(pygame.sprite.Sprite):
#classmethod
def fromImgRect(cls, rect, image, collideable = True):
return cls(rect.left, rect.top, rect.width, rect.height, image, collideable)
def __init__(self, x, y, w, h, image, collideable = True):
self.rect = pygame.rect.Rect(x,y,w,h)
self.collideable = collideable
self.image = image
Then I have the following child class:
class Doodad(WorldObject):
def __init__(self,c1x, c1y, c2x, c2y, color = (200,0,180)):
self.color = color
self.rect = orderPoints(c1x, c1y, c2x, c2y)
x1 = self.rect.left
y1 = self.rect.top
w = self.rect.width
h = self.rect.height
super(Doodad, self).__init__(x1,y1,w,h,self.surface, False)
This works just fine, however it is annoying to have to unpack self.rect like this all throughout my code, instead of just doing it once in the class method. This is happening in many places throughout my project, where several of my methods return a rectangle object, but I need to pass coordinates to a super constructor. It doesn't look like its possible to have everything return either coordinates or a rectangle, sometimes it just makes more sense to do one or the other. Since python doesn't support overloading methods, I'd like to be able to use the class method to initialize the object. However I haven't been able to figure out the syntax. Is this possible? If so, how?
In your situation, I would add a method for "sub-initializing". This would post-process the given data:
class WorldObject(pygame.sprite.Sprite):
#classmethod
def fromImgRect(cls, rect, *a, **k):
return cls(rect.left, rect.top, rect.width, rect.height, *a, **k)
def __init__(self, x, y, w, h, image, collideable=True):
self._init_coords(x, y, w, h)
self.collideable = collideable
self.image = image
def _init_coords(self, x, y, w, h):
self.rect = pygame.rect.Rect(x,y,w,h)
Then you can have the following child class:
class Doodad(WorldObject):
def _init_coords(self, c1x, c1y, c2x, c2y):
self.rect = orderPoints(c1x, c1y, c2x, c2y)
def __init__(self,c1x, c1y, c2x, c2y, color=(200, 0, 180)):
super(Doodad, self).__init__(c1x, c1y, c2x, c2y, self.surface, False)
self.color = color
Besides, you might want to have
def unpack_rect(rect):
return rect.left, rect.top, rect.width, rect.height
You can even have
class WorldObject(pygame.sprite.Sprite):
def __init__(self, *a, **k):
if hasattr(a[0], 'left'):
rect = a[0]
self._init_coords(rect.left, rect.top, rect.width, rect.height)
rest = a[1:]
else:
self._init_coords(*a[0:4])
rest = a[4:]
self._init_rest(*rest, **k)
def _init_coords(self, x, y, w, h):
self.rect = pygame.rect.Rect(x,y,w,h)
def _init_rest(self, image, collideable=True):
self.collideable = collideable
self.image = image
class Doodad(WorldObject):
def _init_coords(self, c1x, c1y, c2x, c2y):
self.rect = orderPoints(c1x, c1y, c2x, c2y)
def _init_rest(color=(200, 0, 180)):
super(Doodad, self)._init_rest(self.surface, False)
self.color = color
(I didn't change self.surface here, but it is not defined at this moment. You should change that.)

Inheritance error with Python

I have the following code.py file:
class Shape:
def __init__(self, x, y):
self.x = x
self.y = y
def move(self, delta_x, delta_y):
self.x += delta_x
self.y += delta_y
class Square(Shape):
def __init__(self, side=1, x=0, y=0):
super().__init__(x, y)
self.side = side
class Circle(Shape):
def __init__(self, rad=1, x=0, y=0):
super().__init__(x, y)
self.radius = rad
I'm running the code in the Python interpreter like this:
>>> import code
>>> c = code.Circle(1)
I'm getting this error:
Traceback (most recent call last):<br>
...<br>
File "code.py", line 18, in __init__<br>
super().__init__(x, y)<br>
TypeError: super() takes at least 1 argument (0 given)<br>
I don't understand why I'm getting this error. I'm specifying a rad value of 1 and I would assume that since I didn't specify x and y values, Circle should be using the default values of x=0 and y=0 and passing them to Shape via the super() function. What am I missing?
BTW, I'm using Python 2.7.1.
Thanks.
super requires an argument and this is exactly what the error message is saying. In your case you need to use super(Circle, self) and super(Square, self).
For the gory details you can see this SO question or you can just check the official documentation.
Note that unless you want to do funny things the code can be simplified in
Shape.__init__(self, x, y)
in both cases. Until you understand super and why it can be useful I would suggest to simply stay away from it. You can live an happy life as a productive Python programmer without touching that.
Use super(Shape, self) instead, you can help(super) in python.
Finally fixed it. :D Searching through python docs and old Stackoverflow posts for the win.
class Shape(object):
def __init__(self, x, y):
self.x = x
self.y = y
def move(self, delta_x, delta_y):
self.x += delta_x
self.y += delta_y
class Square(Shape):
def __init__(self, side=1, x=0, y=0):
super(Square,self).__init__(x, y)
self.side = side
class Circle(Shape):
def __init__(self, rad=1, x=0, y=0):
super(Circle,self).__init__(x, y)
self.radius = rad
c = Circle(5)
This works. You need to use new style classes by making the top parent (Shape) inherit from object.
References:
http://docs.python.org/reference/datamodel.html#newstyle
Chain-calling parent constructors in python
Here's some code that does what you need, you also need to be using the "new style class" meaning the base type needs to inherit from object:
class Shape(object):
def __init__(self, x, y):
self.x = x
self.y = y
def move(self, delta_x, delta_y):
self.x += delta_x
self.y += delta_y
class Square(Shape):
def __init__(self, side=1, x=0, y=0):
super().__init__(x, y)
self.side = side
class Circle(Shape):
def __init__(self, rad=1, x=0, y=0):
super(Circle, self).__init__(x, y)
self.radius = rad
P.S. I only fixed Circle and left Square for you to fix.

Categories