Python3 custom update functions for lists/dictionary - python

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?

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']

How do you get value from another class?

I am starting to learn about object-oriented programming and I'm having a rough time with it.
I have some questions about it:
class Record:
"""Represent a record."""
def __init__(self, category, name, value):
self._category = category
self._name = name
self._value = value
# 1. Define the formal parameters so that a Record can be instantiated
# by calling Record('meal', 'breakfast', -50).
# 2. Initialize the attributes from the parameters. The attribute
# names should start with an underscore (e.g. self._amount)
#property
def amount (self):
return self._value
# Define getter methods for each attribute with #property decorator.
# Example usage:
# >>> record = Record('meal', 'breakfast', -50)
# >>> record.amount
# -50
class Records:
"""Maintain a list of all the 'Record's and the initial amount of money."""
def __init__(self):
# 1. Read from 'records.txt' or prompt for initial amount of money.
# 2. Initialize the attributes (self._records and self._initial_money)
# from the file or user input.
self._records =[]
self._money = 0
fh = open(file='records.txt', mode='r')
if ',' not in i:
self._money = int(i.strip())
else:
raw_line = i.strip()
category, activity, value = raw_line.split(',')
x = Record(category, activity, value)
self._records.append(x)
print("Welcome back!")
fh.close()
#property
def view(self):
for i in self._records:
print(Record._category)
#1. Print all the records and report the balance.
x = Records()
Records.view()
I wanted to print out value from my newly acquired list but I don't know how to pass my data from class Records to class Record for processing. How should I approach this?

Object Oriented Programming: Problem with self in the Constructor, Python 3

I'm currently trying to learn python and am attempting to understand classes and I've ran into a weird problem.
class card:
def __init__(self, s, num, name):
self.suit = s
self.number = num
self.name = name
class aceCard(card):
def __init__(s):
card.__init__(s, 0, "ace of " + s)
value1 = 1
valye11 = 11
class numCard(card):
def __init__(s, num):
name = num, " of ", s
card.__init__(s, num, name)
value = num
class faceCard(card):
def __init__(s, num):
if (num == 11):
name = "jack of " + s
elif (num == 12):
name = "queen of " + s
elif (num == 13):
name = "king of " + s
card.__init__(s, num, name)
value = 10
class suits:
cards = []
def __init__(s):
cards = [aceCard.__init__(s)]
n = 1
while n<11:
cards.append(numCard.__init__(s, n))
n+=1
while n<14 and n>10:
cards.append(faceCard.__init__(s, n))
n+=1
class deck:
adeck = []
def __init__():
adeck = [suits.__init__("clubs"), suits.__init__("diamonds"), suits.__init__("hearts"), suits.__init__("spades")]
def print():
for suitses in adeck:
for cardses in suitses:
print(cardses.name)
deck1 = deck.__init__()
deck1.print()
The error message from the Spyder IDE is
runfile('###', wdir='###')
Traceback (most recent call last):
File "###", line 55, in <module>
deck1 = deck.__init__()
File "###", line 46, in __init__
adeck = [suits.__init__("clubs"), suits.__init__("diamonds"), suits.__init__("hearts"), suits.__init__("spades")]
File "###", line 34, in __init__
cards = [aceCard.__init__(s)]
File "###", line 10, in __init__
card.__init__(s, 0, "ace of " + s)
TypeError: __init__() missing 1 required positional argument: 'name'
The part that confuses me is the fact that the interpreter expects self to be used like a normal parameter rather than being skipped over. Pls help.
Edit:
My question doesn't seem to be fixed by anything I search. My problem is that everything I'm checking to teach me tells me to use as self as a parameter and in the code I'm to use it like "this" in java. However, when I call the constructor, I am to ignore self as the first parameter and use the succeeding ones only. What is the problem there that I'm missing?
There's several issues with your code, but the root of you problem is that you don't know how to construct parent instances.
Python uses super() to do this:
class Card:
def __init__(self, s, num, name):
self.suit = s
self.number = num
self.name = name
class AceCard(Card):
def __init__(self, s):
super().__init__(s, 0, "ace of " + s)
# This does nothing. Just creates local variables which are discarded
# once the function ends.
value1 = 1
valye11 = 11
I can recommend this tutorial as a good intro to Python's OOP implementation.

Python AssertionError creating a class

I have a class User, and a class Theme. The user class can create a Theme, add a Theme to the Theme's dictionary, and should be able to return the dictionary of themes. I'm really new to python so I'm having trouble with the python logic/syntax
class User:
def __init__(self, name):
self.themes = {}
def createTheme(self, name, themeType, numWorkouts, themeID, timesUsed):
newTheme = Theme(name, themeType, numWorkouts, themeID, timesUsed)
return newTheme
and my Theme class:
class Theme:
def __init__(self, name, themeType, numWorkouts, themeID, timesUsed):
#themeType: 1 = genre, 2 = artist, 3 = song
self.name = name
self.themeType = themeType
self.numWorkouts = numWorkouts
self.themeID = themeID
self.timesUsed = timesUsed
I run the test in testUser:
## test createTheme
theme1 = Theme("theme", 2, 5, 1, 0)
self.assertEqual(usr1.createTheme("theme", 2, 5, 1, 0), theme1)
but I get -
Traceback (most recent call last):
File "/Tests/testUser.py", line 52, in test
self.assertEqual(usr1.createTheme("theme", 2, 5, 1, 0), theme1)
AssertionError: !=
I am not sure what I'm doing wrong, can anyone please help?
(Also, I have the following methods in User, but haven't been able to test them yet since my createTheme doesn't work, but I could use some help to see if there are errors in my logic/syntax:
# returns dict
# def getThemes(self):
# return self.themes
#
# def addTheme(self, themeID, theme):
# if theme not in self.themes:
# themes[themeID] = theme
#
# def removeTheme(self, _theme):
# if _theme.timesUsed == _theme.numWorkouts:
# del themes[_theme.themeID]
What is happening
When attempting to determine if two objects are equal, say obj1 == obj2, Python will do the following.
It will first attempt to call obj1.__eq__(obj2), that is a method
defined in the class of obj1 which should determine the logic for
equality.
If this method does not exist, or return NotImplemented, then
Python falls back on calling obj2.__eq__(obj1).
If this is still not conclusive, Python will return id(obj1) == id(obj2),
i.e. it will tell you if both values are the same object in memory.
In your test, Python has to fall back to the third option and your object are two different instances of the class Theme.
What you want to happen
If you expect objects Theme("theme", 2, 5, 1, 0) and usr1.createTheme("theme", 2, 5, 1, 0) to be equal because they have the same attributes, you have to define the Theme.__eq__ method like so.
class Theme:
def __init__(self, name, themeType, numWorkouts, themeID, timesUsed):
#themeType: 1 = genre, 2 = artist, 3 = song
self.name = name
self.themeType = themeType
self.numWorkouts = numWorkouts
self.themeID = themeID
self.timesUsed = timesUsed
def __eq__(self, other)
# You can implement the logic for equality here
return (self.name, self.themeType, self.numWorkouts, self.themeID) ==\
(other.name, other.themeType, other.numWorkouts, other.themeID)
Note that I am wrapping the attributes in tuples and I then compare the tuples for readability, but you could also compare attributes one by one.

Parent and child classes unexpected keyword argument

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.

Categories