calling a parent class constructor from a subclass in python - python

I am trying to use subclasses of a python class to implement [more] specific behaviour for a [very simple] game.
Here's some classes used in the code declared at the beginning:
from enum import Enum
class direction(Enum):
LEFT = ','
RIGHT = '.'
class bomb:
def __init__(self,xval,yval):
self.x = xval
self.y = yval
self.fired = False
After these classes, here's the root class of the class that [seems to be] causing a problem:
class character:
# default constructor
def __init__(self,xval,yval):
self.x = xval
self.y = yval
self.alive = True
self.bomb = bomb(self.x,self.y)
def fire_bomb(self):
self.bomb.x = self.x
self.bomb.y = self.y
self.bomb.fired = True
and here's the subclass where the error appears subclass:
class baddie(character):
def __init__(self,xval,yval):
super().__init__(xval, yval)
self.currentDirection = direction().RIGHT
But when I try and create an array of baddies:
baddie_list = [baddie(1,1)], baddie(3,1), baddie(5,1), baddie(7,1), baddie(9,1), baddie(11,1)]
it's bombing out with the error:
Traceback (most recent call last):
File "pyinvaders.py", line 59, in <module>
baddie_list = [baddie(1,1)] #, baddie(3,1), baddie(5,1), baddie(7,1), baddie(9,1),
baddie(11,1)]
File "pyinvaders.py", line 42, in __init__
self.currentDirection = direction().RIGHT
TypeError: __call__() missing 1 required positional argument: 'value'
I'm not sure what I'm doing wrong here.
The error appears to occur when creating the array of baddie objects, which leads me to examine the constructor for the baddie.
Can anyone give some pointers on how I call the parent class constructor from the child class?
thanks heaps,
David

After all that, the error wasn't in the call to the parent constructor at all!
The error was on the line:
self.currentDirection = direction().RIGHT
which should be:
self.currentDirection = direction.RIGHT
That is, no brackets needed [smacks forehead with palm].
Thanks buran [especially]. You were spot on right - that's where the problem was!

Related

Avoiding too much "self." when working with classes methods

I have created this class that works as expected, I want only to expose one method, get_enriched_dataso the other are pretty much private w/ the underscore.
The functionality works, just pretty convinced I am not doing the most pythonic/OOP way:
class MergeClients:
def __init__(self,source_df,extra_info_df,type_f):
self.df_all = pd.merge(source_df,extra_info_df, on='clientID', how='left')
self.avg_age = self._get_avg_age()
self.type_f = 'Medium'
def _filter_by_age(self, age):
return self.df_all[self.df_all['Age'] > age]
def _filter_by_family_type(self, f_type):
return self.df_all[self.df_all['familyType'] == f_type]
def _get_avg_age(self):
return self.df_all['Age'].mean()
def get_enriched_data(self):
self.df_all = self._filter_by_age(self.avg_age)
self.df_all=self._filter_by_family_type(self.type_f)
return self.df_all
But I find the code looks so ugly with so many self references, for example in the get_enriched_datamethod there are three self references per line, how can I correct this? Any direction on how to correctly Python classes is welcome.
Edit:
Example of working code:
main_df = pd.DataFrame({'clientID':[1,2,3,4,5],
'Name':['Peter','Margaret','Marc','Alice','Maria']})
extra_info = pd.DataFrame({'clientID':[1,2,3,4,5],'Age':[19,35,18,65,57],'familyType':['Big','Medium','Single','Medium','Medium']})
family_stats = MergeClients(main_df,extra_info,'Medium')
family_filtered = family_stats.get_enriched_data()
There are some odd things about your code. I will point out one thing about instances: every method has access to all attributes, so you don't always need to pass them as parameters:
class MergeClients:
def __init__(self,source_df,extra_info_df,type_f):
self.df_all = pd.merge(source_df,extra_info_df, on='clientID', how='left')
self.avg_age = self._get_avg_age()
self.type_f = 'Medium'
def _filter_by_age(self): #No need for age param
return self.df_all[self.df_all['Age'] > self.avg_age]
def _filter_by_family_type(self): #No need for f_type param
return self.df_all[self.df_all['familyType'] == self.type_f]
def _get_avg_age(self):
return self.df_all['Age'].mean()
def get_enriched_data(self):
self.df_all = self._filter_by_age()
self.df_all = self._filter_by_family_type()
return self.df_all
Since the two methods in question: _filter_by_age() and _filter_by_family_type() are private by convention, this means that clients of your class are not expected to call them. So if only other methods of this class call these methods and only the ones you have shown, then there is no need to pass parameters which are already attributes.
Alternatively there is the argument that for other private methods where sometimes they should use attributes, but at other times they should take a parameter, then I would make those methods take a parameter as you had originally.
Functions declared within a Python Class can be effectively made 'private' by preceding the name with double underscore. For example:
class Clazz():
def __work(self):
print('Working')
def work(self):
self.__work()
c = Clazz()
c.work()
c.__work()
The output of this would be:
Working
Traceback (most recent call last):
File "/Volumes/G-DRIVE Thunderbolt 3/PythonStuff/play.py", line 575, in
c = Clazz()
AttributeError: 'Clazz' object has no attribute '__work'
In other words, the __work function has been 'hidden'

What is a 'rect style object' and how do I implement it into my code?

I am trying to learn python by making a space game where you travel around the map fighting enemies(drones) that are randomly placed around the map. So far I have been successful in moving the playable spaceship around the map(with rotation), firing lasers, and blitting drones around the map(The drones follow the spaceship around). Though I don't entirely understand classes, I seem to have been able to pull it off so far.
Now, I want to input enemy hp with Laser/Drone collision. I gave the Drone class self.rect = self.image.get_rect() and called an instance of it in the Laser1 class using self.c = pygame.Rect.colliderect(drone.rect) but it just gives me this:
Traceback (most recent call last):
File "space.py", line 85, in <module>
blist.append(Laser1 (beamx, beamy))
File "space.py", line 79, in __init__
self.c = pygame.Rect.colliderect(drone.rect)
TypeError: Argument must be rect style object
I have searched a lot online for a way to fix this but I don't understand it much and nothing seems to work for me. Any help would be greatly appreciated!
Here is my code:
class Drone:
def __init__(self, dronex, droney):
self.x = dronex
self.y = droney
self.hp = dronehp
self.image = pygame.image.load("Androne.png").convert_alpha()
self.rect = self.image.get_rect()
dlist = []
for i in range(10):
dronex = random.randint(0, 7700)
droney = random.randint(0, 4520)
dronehp = 3
dlist.append(Drone (dronex, droney))
drone = Drone(dronex, droney) #-----------instance i think...
class Roid:
def __init__(self, roidx, roidy):
self.x = roidx
self.y = roidy
self.type = random.randint(0, 2)
rlist = []
for i in range(811):
roidx = random.randint(-1000, 9104)
roidy = random.randint(-800, 7200)
rlist.append(Roid (roidx, roidy))
class Laser1:
def __init__(self, beamx, beamy):
self.x = beamx
self.y = beamy
self.laser1 = pygame.image.load("laser1.png").convert_alpha()
self.rect = self.laser1.get_rect()
self.c = pygame.Rect.colliderect(drone.rect) #---line 79...
blist = []
for i in range(2):
beamx = batx
beamy = baty
blist.append(Laser1 (beamx, beamy)) #---line 85...
class Laser2:
def __init__(self, beamx2, beamy2):
self.x2 = beamx2
self.y2 = beamy2
self.laser1 = pygame.image.load("laser1.png").convert_alpha()
self.rect = self.laser1.get_rect()
self.c = pygame.Rect.colliderect(drone.rect)
b2list = []
for i in range(2):
beamx2 = batx
beamy2 = baty
b2list.append(Laser2 (beamx2, beamy2))
Also, this is my first question to ask on here. If there is anything I can do to make this question better do tell. I will except any and all feedback!
To answer the title: A rect style object is a pygame.Rect, a tuple or a list with 4 elements. So you can usually pass a 4-tuple or list to methods that expect a pygame.Rect as well.
As Paul Cornelius mentioned, the exception is raised because you use the colliderect method of the pygame.Rect class, but should instead use the colliderect of the self.rect instance.
self.c = self.rect.colliderect(drone.rect)
You can actually use pygame.Rect.colliderect directly and pass the two rects that you want to check, but that's unusual and would be a bit confusing for everyone else.
# `self.rect` is passed as the `self` argument.
self.c = pygame.Rect.colliderect(self.rect, drone.rect)
Paul Cornelius is in the right direction. Since you're learning python I'll explain briefly. The colliderect method is implemented in C:
static PyObject*
rect_colliderect (PyObject* oself, PyObject* args)
{
PyRectObject* self = (PyRectObject*)oself;
GAME_Rect *argrect, temp;
if (!(argrect = GameRect_FromObject (args, &temp)))
return RAISE (PyExc_TypeError, "Argument must be rect style object");
return PyInt_FromLong (DoRectsIntersect (&self->r, argrect));
}
Since you called it through the class definition and not an instance, drone.rect was assigned to oself by the python wrapper to this method, and args was probably assigned some equivalent of NULL. Hence the error message you saw (and not a 'missing required positional argument' as one would expect).
Don't have any experience with Pygame, but with a little searching it seems the problem you are having is a result of passing an improper argument to your pygame.Rect.colliderect() on line 79. The object you are passing the pygame.Rect.colliderect() method is not being read as a Rectangle object. Try printing the object drone.rect data and object type to see where the problem might be.

Getting a value from a class using a return statement within the class

class Player1:
base_HP = 300
def getBHP(self):
return self.base_HP
jogador1 = Player1
jogador1_hp = jogador1.getBHP() #Functions and Class calls must end with brackets.
print(jogador1_hp)
That's the code I'm using to get the player HP and i want to save it at jogador1_hp.
How ever this is what iam getting:
C:\Users\joaol\AppData\Local\Programs\Python\Python36-32\python.exe C:/Users/joaol/PycharmProjects/FirstProgram/Main.py
<function Player1.getBHP at 0x02C131E0>
Process finished with exit code 0
Even if i do as below, I'm still getting a blank console.
class Player1:
base_HP = 300
def getBHP(self):
print(self.base_HP)
jogador1 = Player1
jogador1.getBHP
EDIT: I fix it ,i just needed to add "()" when i create the object!
jogador1 = Player1()
jogador1_hp = jogador1.getBHP()
You have to call methods for them to execute.
jogador1_hp = jogador1.getBHP()
You don't instantiate the Player1 class. In your code, jogador1 is just another name for the Player1 class. You should call the class to instantiate it, like: jogador1 = Player1()
If i use jogador1_hp = jogador1.getBHP() i get this :
C:\Users\joaol\AppData\Local\Programs\Python\Python36-32\python.exe C:/Users/joaol/PycharmProjects/FirstProgram/Main.py
Traceback (most recent call last):
File "C:/Users/joaol/PycharmProjects/FirstProgram/Main.py", line 19, in <module>
jogador1_hp = jogador1.getBHP()
TypeError: getBHP() missing 1 required positional argument: 'self'
Process finished with exit code 1

Pygame, inheritance issue

I am either new nor experience in python and i find myself in a fat problem that even after many hours in Google and Bing i can find the answer for. My problem starts with this code:
class Rectangulo(object):
def __init__ (self, x, y, color, largo, alto, cambio_x, cambio_y):
self.alto = alto
self.largo = largo
self.color = color
self.cambio_x = cambio_x
self.cambio_y = cambio_y
self.x = x
self.y = y
def dibujar(self):
pygame.draw.rect(pantalla, self.color, (self.x, self.y, self.alto, self.largo))
def mover(self):
self.x += self.cambio_x
self.y += self.cambio_y
class Elipse(Rectangulo):
def __init__(self):
Rectangulo.__init__(self)
def dibujar (self):
pygame.draw.ellipse(pantalla, Rectangulo.color,(Rectangulo.x, Rectangulo.y, Rectangulo.alto, Rectangulo.largo))
THis is the most important piece of code right now. I have find the problem laid in Elipse and have tried many ways to make the inheritance to work, but the console continue showing this message.
Traceback (most recent call last):
File "/home/josh/Escritorio/Codigo python/Jueguito.py", line 63, in <module>
miEl = Elipse(x,y,VERDE,alto,largo,cam_x,cam_y)
TypeError: __init__() takes 1 positional argument but 8 were given
Everytime i tried to call the class function dibujar() with this code:
for item in range(10):
x = random.randrange(685)
y = random.randrange(485)
alto = random.randrange(20, 71)
largo = random.randrange(20, 71)
cam_x = random.randrange(1, 2)
cam_y = random.randrange(-3, 3)
miObjeto = Rectangulo(x,y,VERDE,alto,largo,cam_x,cam_y)
miLista.append(miObjeto)
miEl = Elipse(x,y,VERDE,alto,largo,cam_x,cam_y)
miEl variable used to have their own for loop but i thought in this way would be less confusing for me. I still can't figure out what is happening. I fear i need some help.
Your Eclipse instance is failing to initialise, because when it calls the initialise of its base class Rectangulo, that class takes 7 parameters (x, y color, ... etc) and you aren't providing any.
So you have a number of options, but the two most common approaches would be:
Pass the same 7 params into Eclipse's __init__ method, and then pass those same params into the call to Rectangulo.__init__(self, ...)
Decide on the params to be used for the Rectangulo within the Elipse e.g. Rectangulo.__init__(self, 1, 2, "red", ...).
Generally speaking you will probably want the first option. e.g.
class Elipse(Rectangulo):
def __init__(self, x, y, color, largo, alto, cambio_x, cambio_y):
Rectangulo.__init__(self, x, y, color, largo, alto, cambio_x, cambio_y)
If your Elipse class needs no additional parameters of its own, then you can simplify the above a little by doing this:
class Elipse(Rectangulo):
def __init__(self, *args, **kwargs):
Rectangulo.__init__(self, *args, **kwargs)
This will basically pass any and all arguments given to the instantiation of Elipse through to the Elipse.__init__ call.

Calling class functions through list

I was attempting to place certain class variables within a list. Each class has a method known as update and considering they each do the exact same thing I though it would be allot more convenient to simply place them in a list and call the method that way.
The method I am trying to call is known as update and this is the error that I get:
Traceback (most recent call last):
File "C:\Users\GoodPie\Desktop\New Game Dev\main.py", line 177, in <module>
initialize_game()
File "C:\Users\GoodPie\Desktop\New Game Dev\main.py", line 61, in initialize_game
start_menu(debugging, screen, clock, image_cache)
File "C:\Users\GoodPie\Desktop\New Game Dev\main.py", line 88, in __init__
self.init_start_screen()
File "C:\Users\GoodPie\Desktop\New Game Dev\main.py", line 115, in init_start_screen
self.update_group.append[New_Game_Button, Load_Game_Button, Quit_Game_Button, Settings_Button]
TypeError: 'builtin_function_or_method' object is not subscriptable
Here is the code I am trying to use
Entities.py:
class Quit_Game_Button(Entity):
def __init__(self, x, y, image_cache):
Entity.__init__(self)
self.image_cache = image_cache
self.image = function.get_image("images/Quit_Game.png", self.image_cache)
self.image.convert()
self.rect = Rect(x, y, 150, 30)
def update(self, mouse_position):
if self.rect.collidepoint(mouse_position):
self.image = function.get_image("images/Quit_Game_Hover.png", self.image_cache)
else:
self.image = function.get_image("images/Quit_Game.png", self.image_cache)
def check_click(self, clicked, mouse_position):
quit = False
if self.rect.collidepoint(mouse_position):
if clicked:
quit = True
return quit
class Settings_Button(Entity):
def __init__(self, x, y, image_cache):
Entity.__init__(self)
self.image_cache = image_cache
self.image = function.get_image("images/Settings_Button.png", self.image_cache)
self.image.convert()
self.rect = Rect(x, y, 50, 50)
def update(self, mouse_position):
if self.rect.collidepoint(mouse_position):
self.image = function.get_image("images/Settings_Button_Hover.png", self.image_cache)
else:
self.image = function.get_image("images/Settings_Button.png", self.image_cache)
main.py
self.update_group.append[New_Game_Button, Load_Game_Button, Quit_Game_Button, Settings_Button]
mouse_position = pygame.mouse.get_pos()
#Updating Entities on the screen
for entity in self.update_group:
entity.update(mouse_position)
Just so it's clear, the functions do exist. I was just wondering if I was going about calling methods the write way through a list/array? If not could someone please point me in the right direction? :)
You are trying to assign one to one(1) and two to two(), that results in a referenced before assignment error So change the class Names to something more suitable like One and Two
Also It is strictly recommended(for CapWords convention see pg4 here) to always start a python class name with a uppercase character
Corrected code would look like: (I've implemented the str method)
class One:
def __init__(self, x):
self.x = x
def update(self):
self.x += 1
def __str__(self):
return str(self.x)
class Two:
def __init__(self, x):
self.x = x
def update(self):
self.x += 1
def __str__(self):
return str(self.x)
def test():
one = One(1)
two = Two(2)
update_list = [one, two]
for i in update_list:
i.update()
print i
test()
Output:
2
3
This is a simple error.
self.update_group.append[New_Game_Button, Load_Game_Button, Quit_Game_Button, Settings_Button]
should be
self.update_group.extend([New_Game_Button, Load_Game_Button, Quit_Game_Button, Settings_Button])
You're trying to index append where you should call it. Second point is, that append takes only one parameter, so you should use extend.
But you're still operating on the classes, not instances of the classes.
The issue did not lie within the actual for loop for calling the update function within the class, it occurred through the appending off the class instances to the list. Therefore to solve this problem I tried:
self.update_group = [New_Game_Button, Load_Game_Button, Quit_Game_Button, Settings_Button]
and it worked perfectly. I don't know what the issue is with the appending to the array, but having a fixed list/array seems to work just fine for me. Credits to #Matthias for actually making me look at the method for adding the instances to the array.

Categories