Parent and child classes unexpected keyword argument - python

I was experimenting with parent and child classes in python. I got an error that I can't seem to solve, both the error and code are posted below. If you could post a reason why this is happening and an edit of my code how to fix this it would be much appreciated.
# Classes Example
class vehicle():
def __init__(self,name,weight,wheels,color):
self.name = name
self.weight = weight
self.wheels = wheels
self.color = color
def __str__(self):
return("Hi, iam a vehicle called " + self.name)
def wow(self):
return(self.weight/self.wheels)
class car(vehicle):
def __init__(self,doors,quantity,mpg):
self.doors = doors
self.quantity = quantity
self.mpg = mpg
def distance(self):
return(self.quantity/self.mpg)
# Main Program
object1 = vehicle("Audi A3",1000,4,"blue")
print(object1)
print(object1.name)
object1.name = "Audi S3"
print(object1.name)
print(object1.weight)
object2 = vehicle(name = "Claud Butler" , color = "Red" , wheels = 2, weight = 20)
print(object2)
print(object2.wow())
object3 = car(name = "Burty", color = "Pink" , wheels = 3, weight = 500, doors = 3 , quantity = 10, mpg = 1000)
print(object3.color)
print(object3.wow())
print(object3.distance())
I get the following error:
Traceback (most recent call last):
File "H:\my documents\Computing\Class example.py", line 39, in <module>
object3 = car(name = "Burty", color = "Pink" , wheels = 3, weight = 500, doors = 3 , quantity = 10, mpg = 1000)
TypeError: __init__() got an unexpected keyword argument 'name'

The error is raised in the following line:
object3 = car(name = "Burty", color = "Pink" , wheels = 3, weight = 500, doors = 3 , quantity = 10, mpg = 1000)
There, you are calling the car constructor with (amongst others) a name parameter. Now, if you look at the define constructor of the car type, you see the following:
class car(vehicle):
def __init__(self,doors,quantity,mpg):
self.doors = doors
self.quantity = quantity
self.mpg = mpg
As you can see, there is no name parameter in the parameter list.
I assume that you expected the car child class to inherit all the constructor parameters from the parent vehicle class but that’s not actually the case. Unless you explicitely duplicate those parameters, they won’t be there. You also have to call the parent’s constructor explicitely. All in all, your car constructor should look like this:
def __init__(self, name, weight, wheels, color, doors, quantity, mpg):
# call the base type’s constructor (Python 3 syntax!)
super().__init__(name, weight, weels, color)
self.doors = doors
self.quantity = quantity
self.mpg = mpg

The method
__init__(self,doors,quantity,mpg):
does not have an argument called name. If you want it, you should add it.

Related

how to get attribute information through another attribute in a class

I have a class like this:
class School:
instances = []
def __init__(self, name, number, hour, income):
self.__class__.instances.append(weakref.proxy(self))
self.name = name
self.number = number
self.hour = hour
self.income = income
And an instance like this:
n1 = School('Namjoo', 114, 50, 30)
how can I extract the name attribute by having 114, 50 or 30?
I can't use a dictionary as not all the data stays unique
If you want to find the name of any class instances that have a particular value set for another attribute, you can walk the instances list and check the listed object attributes. For example:
print([x.name for x in School.instances if x.number == 114 or x.hour == 50 or x.income == 30])
# ['Namjoo']

Why python first prints out the class method?

I have written a sample code like below. I wonder why the method prints out first even it is on lower lines...
class Dog():
species = "Mammals"
legs = 4
def __init__ (self,breed, name, spots, height):
self.breed = breed
self.name = name
self.spots = spots
self.height = height
def bark(self,number):
for i in range(number):
print("My name is {}".format(self.name))
MyDog = Dog(breed = "Huskie", name = "MyDog", spots = True, height = "Tall")
print(MyDog.species,MyDog.legs,MyDog.breed,MyDog.name,MyDog.spots,MyDog.bark(3),MyDog.height,sep="\n")
print is just a function like any other, and Python evaluates all arguments to a function before calling that function. Otherwise the function wouldn't know the values of its arguments and wouldn't know what to do. So MyDog.bark(3) must be evaluated before it's possible to call print(..., MyDog.bark(3), ...).

Python: Can't copy pygame surface objects

I have a class:
class personInfo:
def __init__(self,name,age,height,hairColour,face):
self.name = name
self.age = age
self.height = height
self.hairColour = hairColour
self.face = face
I have several images here that I load in using the pygame module.
yellowFace = pygame.image.load("happy.png")
blueFace = pygame.image.load("sad.png")
redFace = pygame.image.load("angry.png")
I created an array that holds instances of that class. Here I am populating it.
personArray = []
while len(array) != 10:
personArray.append(personClass("John",32,6,"Blond",yellowFace))
I would like to store the actual variables of that class (name, age height,hairColour, etc) in a variable I will call "personStorage". However, I can't have that var be mutatable since I need to access that var's values and change them. Doing this can't change the values of any instances inside the personArray. How would I do this?
EDIT:
I also can't seem to be able to Copy this class because I get a type error that says: "can't pickle pygame.surface objects" since I have a value of a surface object within that class.
If I understood what you're trying to do :
class PersonInfo:
def __init__(self,name,age,height,hairColour):
self.name = name
self.age = age
self.height = height
self.hairColour = hairColour
def toList(self):
return [self.name, self.age, self.height, self.hairColour]
By the way, class names' first letter is always uppercase.
EDIT : To achieve what you're trying to do :
old = PersonInfo("old", 1, 2, "blue")
new = PersonInfo(*old.toList())
new.name = "new"
print(old.name) #'old'
Use the copy module:
The function copy() shallow copy operation on arbitrary Python objects.
import copy
class PersonInfo:
def __init__(self, name, age, height, hair_colour):
self.name = name
self.age = age
self.height = height
self.hairColour = hair_colour
personArray = []
for x in range(20, 24):
personArray.append(PersonInfo("John", x, 6, "Brown"))
personStorage = copy.copy(personArray[2])
personStorage.name = "Ana"
print("\rpersonArray[2].name is %s\n"
"\rpersonStorage.name is %s"
% (personArray[2].name, personStorage.name))
I have created setters and getters to get the data.Also you can create a copy of the instance that holds the data.
from copy import deepcopy
class PersonInfo:
def __init__(self,name,age,height,hairColour):
self.name = name
self.age = age
self.height = height
self.hairColour = hairColour
x = PersonInfo("Mario",34,1.70,"blue")
print(x.height) # prints 1.70
x1 = deepcopy(x)
print(x1.age)

Python3 custom update functions for lists/dictionary

I'm creating a terminal based game and after using an answer from my last question, I realized I ran into another problem. I needed to figure out how to properly update stats on a character based on set class, and their solution worked. But now I need to figure out how to update it if it's not a number. So far I generated one for strings, but now I need one for a list of data.
For example, the player class so far consists of a base character with default stats and values.
class BaseCharacter:
#define what to do when the object is created, or when you call player = BaseCharacter()
def __init__(self):
#generate all the stats. these are the default stats, not necessarily used by the final class when player starts to play.
#round(random.randint(25,215) * 2.5) creates a random number between 25 and 215, multiplies it by 2.5, then roudns it to the nearest whole number
self.gold = round(random.randint(25, 215) * 2.5)
self.currentHealth = 100
self.maxHealth = 100
self.stamina = 10
self.resil = 2
self.armor = 20
self.strength = 15
self.agility = 10
self.criticalChance = 25
self.spellPower = 15
self.speed = 5
self.first_name = 'New'
self.last_name = 'Player'
self.desc = "Base Description"
self.class_ = None
self.equipment = [None] * 6
The update function that I used from my last question goes as follows:
#define the function to update stats when the class is set
def updateStats(self, attrs, factors):
#try to do a function
try:
#iterate, or go through data
for attr, fac in zip(attrs, factors):
val = getattr(self, attr)
setattr(self, attr, val * fac)
#except an error with a value given or not existing values
except:
raise("Error updating stats.")
So when I create a WarriorCharacter based off of the BaseCharacter...
class WarriorCharacter(BaseCharacter):
#define data when initialized, or the Object is created
def __init__(self, first_name, last_name):
super().__init__()
#update the class value since its a copy of BaseCharacter
self.class_ = 'Warrior'
#update the first name
self.first_name = first_name
#update the last name
self.last_name = last_name
#update description value
self.desc = 'You were born a protector. You grew up to bear a one-handed weapon and shield, born to prevent harm to others. A warrior is great with health, armor, and defense.'
self.updateStats(['stamina', 'resil', 'armor', 'strength', 'speed'], [1.25, 1.25, 1.35, 0.75, 0.40])
... I can run self.updateStats(['stamina', 'resil', 'armor', 'strength', 'speed'], [1.25, 1.25, 1.35, 0.75, 0.40]) in the Warrior class to properly update the new stats of the created player.
However I had to slightly revise the updateStats function for the shops class, because it uses mostly strings instead of numbers, by updating the for loop.
for attr, fac in zip(attrs, factors):
setattr(self, attr, fac)
With the shop class...
class BaseShop:
def __init__(self):
#generate shop information
self.name = "Base Shop"
self.gold = round(random.randint(325, 615) * 2)
self.desc = "Base Description"
self.stock = [None] * random.randint(3, 8)
self.vendor = "Base Vendor"
#set responses
#. . .
... This allows me to call shop.updateShopInfo(["vendor"], ["Test Vendor"]) to change the shops vendor name from 'Base Vendor' to 'Test Vendor'
The Problem
Now I don't know how to revise this function for an array/list data type. As you saw in the example I had a few variables such as self.equipment = [None] * 6 and self.stock = [None] * random.randint(3, 8). I want to be able to update these stats with items from the item class which is going to built like so:
class BaseItem:
def __init__(self):
self.name = "Base Item Name"
self.desc = "Base Item Description"
self.itemType = None
self.itemSize = None
self.stats = {
'Strength': 0,
'Agility': 0,
'Critical Chance': 0,
'Spell Power': 0,
'Speed': 0,
'Health': 0,
'Stamina': 0,
'Armor': 0,
'Resilience': 0
}
self.slot = None
#my attempt at it so far
def updateItemStats(self, attrs, factors):
try:
#iterate, or go through data
for attr, fac in zip(attrs, factors):
val = getattr(self, attr)
setattr(self, attr, val + fac)
#except an error with a value given or not existing values
except:
raise("Error updating stats.")
class OneHandedSword(BaseItem):
def __init__(self):
super().__init__()
self.itemType = "Sword"
self.itemSize = "One Hand"
self.name = "Jade Serpentblade"
self.desc = "Sharp cutlass."
self.slot = "Weapon"
#something like this? would update the stats value of the new item to have +5 strength and +2 stamina
self.update([self.stats["strength"], self.stats["stamina"]], [5, 2])
What I tried
The update function throws this error:
Traceback (most recent call last):
File "/home/ubuntu/workspace/Python/game_Test/test.py", line 33, in <module>
item2 = OneHandedSword()
File "/home/ubuntu/workspace/Python/game_Test/assets/items.py", line 46, in __init__
self.updateItemStats([self.stats["Strength"], self.stats["Stamina"]], [5, 2])
File "/home/ubuntu/workspace/Python/game_Test/assets/items.py", line 33, in updateItemStats
raise("Error updating stats.")
TypeError: exceptions must derive from BaseException
When I take out the try/except, it returns this error:
Traceback (most recent call last):
File "/home/ubuntu/workspace/Python/game_Test/test.py", line 33, in <module>
item2 = OneHandedSword()
File "/home/ubuntu/workspace/Python/game_Test/assets/items.py", line 43, in __init__
self.updateItemStats([self.stats["Strength"], self.stats["Stamina"]], [5, 2])
File "/home/ubuntu/workspace/Python/game_Test/assets/items.py", line 28, in updateItemStats
val = getattr(self, attr)
TypeError: getattr(): attribute name must be string
I then tried editing val = getattr(self, attr) to val = gettattr(self, self.stats[attr] it then returns KeyError: 0
What I need
So I essentially need two update functions, one for updating the item.stats dictionary, and one for updating the stock and equipment lists with item objects. I cannot for the life of me figure out how to do this. Any ideas?

Spawn multiple classes and retrieve them

I'm wondering if there is a "standard" way to spawn multiple instances of a class dynamically and retrieve them?
The below code spawns 10 bikes, with random prices and colours, I'm wondering how I would call them back? I played around with appending to a list but the returned items are strings.
bikes = []
class Bike:
style = ""
price = 0
colour = ""
def __init__(self, style, price, colour):
self.style = style
self.price = price
self.colour = colour
self.description = "a {0} {2} worth £{1}".format(self.colour, self.price, self.style)
def print_details(self):
print("description: {0}".format(self.description))
for b in range(10):
price = random.choice(random_prices)
colour = random.choice(random_colours)
bike = "bike" + str(b)
bike = Bike('bike', price, colour)
bikes.append(bike)
for i in bikes:
print_details(i)
Traceback (most recent call last):
print("description: {0}".format(self.description))
AttributeError: 'str' object has no attribute 'description'
Any time you create a class (or any kind of data) it would need to be referenced somewhere. If it isn't, you can consider it lost.
If you need to create a collection of classes, then a collection is probably a good way to store them:
bikes = []
for b in range(10):
price = random.choice(random_prices)
colour = random.choice(random_colours)
bike = "bike" + str(b)
bike = Bike('bike', price, colour)
bikes.append(bike)
Based on your updated question, I believe the issue is in how you're calling print_details. Because it's a class member, it should be called like this:
for i in bikes:
i.print_details()

Categories