how to access to data from a class which is stored in another class in python? - python-3.x

This is my code. I got a problem when i want to print the information inside the class 'pokemon'
class trainer(object):
def __init__(self, name, pokemons = [], money = 0):
self.name = name
self.pokemons = pokemons
self.money = money
this is my first class which has every pokemon per trainer
class pokemon(object):
def __init__(self, name, attribute, attacks = {}, health = '==========='):
self.name = name
self.attribute = attribute
self.health = health
self.attacks = attacks
The other class where I take the pokemon to import to the other class
class fight():
def __init__(self, fighter1, fighter2):
self.fighter1 = fighter1
self.fighter2 = fighter2
def fighting(self):
if len(Trainer1.pokemons) >= 1 and len(Trainer2.pokemons) >= 1:
print('{} wanna fight against {}'.format(Trainer1.name, Trainer2.name))
keepgoing = True
print('{} got this Pokemons: '.format(Trainer1.name))
i = 0
for i in Trainer1.pokemons:
print(i)
#while (keepgoing):
else:
print('You gotta have pokemons to fight')
return False
I thought that creating a class named fight for getting in battle would be the most wise idea but I'd like to know another method to do it
Pokemon1 = pokemon('Charizard', 'Fire', attacks={'1':'ball fire', '2':'cut', '3':'fire blast', '4':'mega kick'})
Pokemon2 = pokemon('Charmander', 'fire', attacks={'1':'blast', '2':'scratch', '3':'heat', '4':'tear'})
Trainer1 = trainer('Santiago', pokemons=[Pokemon1, Pokemon2])
Pokemon3 = pokemon('Charizard', 'Fire', attacks={'1':'ball fire', '2':'cut', '3':'fire blast', '4':'mega kick'})
Pokemon4 = pokemon('Charmander', 'fire', attacks={'1':'blast', '2':'scratch', '3':'heat', '4':'tear'})
Trainer2 = trainer('Alejandra', pokemons=[Pokemon3, Pokemon4])
Okay my problem is in the class fight. when i want to print the names of the pokemons i get the following message:
Santiago got this Pokemons:
<__main__.pokemon object at 0x000002AAD9B64D00>
<__main__.pokemon object at 0x000002AAD9B92DF0>
i know that the pokemon class has various instances, but how can i access to them?

To make your life easier, I recommend that you implement the __str__ dunder method on pokemon. This will resolve the issue that you are seeing right now, and make future prints of pokemon much easier.
That would look something like this:
class pokemon(object):
def __init__(self, name, attribute, attacks = {}, health = '==========='):
self.name = name
self.attribute = attribute
self.health = health
self.attacks = attacks
def __str__(self):
return "Pokemon: %s (Health: %11s)" % (self.name, self.health)
When you print the 'Charmander' pokemon, it'll look something like this:
Pokemon: Charmander (Health: ===========)
Of course, you can change the return of the __str__ to return whatever you want out of the pokemon.

Related

Why the type of a parameter of a function in a class would change into list?

class Employee:
num_of_emps = 0
raise_amount = 1.04
def __init__(self, first, last, pay):
self.first = first
self.last = last
self.pay = pay
self.email = first + last + '#gmail.com'
Employee.num_of_emps += 1
def fullname(self):
return f'I am {self.first} {self.last}'
def apply_raise(self):
self.pay = int(self.pay * Employee.raise_amount)
#classmethod
def set_raise_amt(cls, amount):
cls.raise_amount = amount
#classmethod
def from_string(cls, emp_str):
first, last, pay = emp_str.split('-')
return cls(first, last, pay)
#staticmethod
def is_workday(day):
if day.weekday() == 5 or day.weekday() == 6:
return False
return True
class Developer(Employee):
raise_amount = 1.50
def __init__(self, first, last, pay, prog_lang):
super().__init__(first, last, pay)
self.prog_lang = prog_lang
class Manager(Employee):
def __init__(self, first, last, pay, employees=None):
super().__init__(first, last, pay)
if employees is None:
self.employees = []
else:
self.employees = employees
def add_emp(self,emp):
if emp not in self.employees:
self.employees.append(emp)
def remove_emp(self,emp):
if emp in self.employees:
self.employees.remove(emp)
def print_emps(self):
for emp in self.employees:
print('--->', emp.full_name())
dev_1 = Developer('John','Doe',30000, 'Python')
dev_2 = Developer('Emily','Smith',23000, 'Java')
# print(help(Developer))
print(dev_1.email)
print(dev_2.email)
print(dev_1.pay)
dev_1.apply_raise()
print(dev_1.pay)
mgr_1 = Manager('Sarah','Smith',34000, [dev_1])
print(type(mgr_1.employees))
print(mgr_1.employees)
print(type(dev_1))
print(type([dev_1]))
print([dev_1])
mgr_1.print_emps()
I recently studied this code on youtube. So basically this code started with a class named 'Employee' at the beginning, and then a subclass called 'Developer' was created. I still able to catch up with the logic behind the code at the moment, but after another subclass called 'Manager' was created, I lost.
I don't know why the parameter,'employees' in the class 'Manager' would suddenly become a list in the end
And I also don't know why the for loop could be able to run
Please help, thank you so much
mgr_1 = Manager('Sarah','Smith',34000, [dev_1])
first='Sarah', last='Smith', pay=34000, employees=[dev_1]
Your parameter is an list

Is there a way to get a random number to repopulate inside of a function after when calling the function again?

I am fairly new to Python and I know values called in a function are only there inside the function. I am trying to have a battle between a player and a boss in a small text game I am writing; however, It just keeps populating the same information each time the function is called. I feel like I am missing something. Any help would be appreciated.
The classes:
class Character:
def __init__(self, name, stats):
self.name = name
self.stats = stats
name = {
"Name": ""
}
stats = {
"Dexterity": "",
"Strength": "",
"Health": 20,
"AC": 16,
"Weapon": "",
}
damage = 2 * random.randrange(1, 7)
ability_check = random.randrange(1, 20)
initiative = random.randrange(1,20)
class Boss:
def __init__(self, name, stats):
self.name = name
self.stats = stats
name = {
"Name": "Gargamel"
}
stats = {
"AC": 16,
"Health": 15,
"Weapon": "Sword"
}
damage = random.randrange(1, 6)
initiative = random.randrange(1,20)
The functions:
def battle():
choice = input("Do you wish to continue fighting or run? F or R ")
if (choice.lower() == 'f'):
boss_battle()
if (choice.lower() == 'r'):
pass
def boss_battle():
print("The skeletal creature grabs a sword from the wall and takes a swing at you...\n")
print(f"Boss init {Boss.initiative}, Character init {Character.initiative}")
while Boss.stats["Health"] > 0 or Character.stats["Health"]:
if (Boss.initiative > Character.initiative):
boss_damage = Boss.damage
current_player_health = (Character.stats["Health"] - boss_damage)
Character.stats.update({"Health": current_player_health})
print(f"The boss did {boss_damage} damage. You now have {current_player_health} hitpoints left.")
if (Character.stats["Health"] <= 0):
print('You died!')
break
battle()
elif (Character.initiative > Boss.initiative):
player_damage = Character.damage + stat_block_str(int)
current_boss_health = Boss.stats["Health"] - player_damage
Boss.stats.update({"Health": current_boss_health})
print(f"You attacked the creature with your {Character.stats['Weapon']} and dealt {player_damage} damage.")
if (Boss.stats["Health"] <= 0):
print(f'Congratulations {Character.name["Name"]}! You have beaten the boss and claimed the treasure!')
break
battle()
You have declared classes with class variables, but have made no class instances, so the values are all fixed due to being initialize once when the class was defined.
To make a class instance, you "call" the class using parentheses, which calls your __init__ function on the instance, which sets instance variables.
Here's a small example:
import random
class Character:
def __init__(self,name):
self.name = name
self.health = 20
self.damage = 2 * random.randrange(1,7)
def attack(self,target):
print(f'{self.name} attacks {target.name}...')
target.health -= self.damage
print(f'{target.name} has {target.health} health remaining.')
# defines how to display the class instance when printed.
def __repr__(self):
return f"Character(name={self.name!r}, health={self.health}, damage={self.damage})"
fred = Character('Fred')
george = Character('George')
print(fred)
print(george)
print(f'{fred.name} can deliver {fred.damage} damage.')
fred.attack(george)
Output:
Character(name='Fred', health=20, damage=4)
Character(name='George', health=20, damage=10)
Fred can deliver 4 damage.
Fred attacks George...
George has 16 health remaining.
While true, that your using class variables when you should better use instance variables (see Instance variables vs. class variables in Python amongst others for details) I don't think it is enough to achieve your goal.
What you describe in the comment happens because the class is only setup once and from there on the class variables like damage etc. are static, meaning the random call is only executed once for the whole program. I would suggest to convert the damage variables to functions like this:
class Character:
def __init__(self,name):
self.name = name
self.health = 20
# just as a simple example
self.base_damage = 2
def damage(self):
return self.base_damage * random.randrange(1,7)
This way you will obtain a new random value for call to the function (battle round) and not just have a static value for damage inside the class.
Assuming you made instances for the character and the boss the call can look like this:
current_boss_health = bossInstance.stats["Health"] - characterInstance.damage()
If you want to take this on step further and make this a bit more pythonic you can use the #property decorator for damage:
#property
def damage(self):
return self.base_damage * random.randrange(1,7)
This way the usage of the function will look even more like your original and it hides the function call:
current_boss_health = bossInstance.stats["Health"] - characterInstance.damage

How to remove an object from list by a value

My problem is that I created a list of students with name and number. The task is now to remove a student by his number. My problem is that my code doesn't work.
Another problem is that it always shows the memory address instead of the value of the object.
Thanks in advance
class Student:
def __init__(self, name, number):
self.name = name
self.number = number
from .student import Student
class Course:
def __init__(self, name, code, credit, student_limit):
self.name = name
self.code = code
self.credit = credit
self.student_limit = student_limit
students = []
def add_student(self, new_student):
self.student = new_student
self.students.append(new_student)
print("Student added" +str(self.students))
def remove_student_by_number(self, student_number):
self.student_number = student_number
if student_number in self.students: self.students.remove(student_number)
print("Student removed" + str(self.students))
from .course import Course
class Department:
def __init__(self, name, code):
self.name = name
self.code = code
courses = []
def add_course(self, course):
self.course = course
self.courses.append(course)
print("Course added" +str(self.courses))
from python import *
def main():
alice = Student("Alice", 1336)
bob = Student("Bob", 1337)
math_dept = Department("Mathematics and Applied Mathematics", "MAM")
math_course = Course("Mathematics 1000", "MAM1000W", 1, 10)
math_dept.add_course(math_course)
math_course.add_student(bob)
math_course.add_student(alice)
math_course.remove_student_by_number(alice.number)
if __name__ == "__main__":
main()
self.students is a list of Student instance so it will print the instance's memory address if the method __str__ is not implemented (see here for example). You should try to print each property like student.name or student.number explicitly.
Anyway you are trying to find student_number in list of Student so of course it will never run the remove line. Instead use if student_number in [student.number for student in self.students] which is looking up the student's number list, not the student list itself. However in this case, you also want to remove the student with the student_number as the input so you may need to use a loop like this:
def remove_student_by_number(self, student_number):
for student in self.students:
if student.number == student_number:
print("Student removed" + str(student.name))
self.students.remove(student)
break

In OOP in python, are different instances of an object when initialised with a default value the same?

I am trying to understand object oriented programming. I am doing this by creating a small poker like program. I have come across a problem whose minimal working example is this:
For this code:
import random
class superthing(object):
def __init__(self,name,listthing=[]):
self.name = name
self.listthing = listthing
def randomlyadd(self):
self.listthing.append(random.randint(1,50))
def __str__(self):
return '\nName: '+str(self.name)+'\nList: '+str(self.listthing)
Aboy = superthing('Aboy')
Aboy.randomlyadd()
print(Aboy)
Anotherboy = superthing('Anotherboy')
Anotherboy.randomlyadd()
print(Anotherboy)
I expect this output :
Name: Aboy
List: [44]
(some number between 1 and 50)
Name: Anotherboy
List: [11]
(again a random number between 1 and 50)
But what I get is:
Name: Aboy
List: [44]
(Meets my expectation)
Name: Anotherboy
List: [44,11]
(it appends this number to the list in the previous instance)
Why is this happening? The context is that two players are dealt a card from a deck. I am sorry if a similar question exists, if it does, I will read up on it if you can just point it out. New to stack overflow. Thanks in advance.
For the non minimal example, I am trying this:
import random
class Card(object):
def __init__(self, suit, value):
self.suit = suit
self.value = value
def getsuit(self):
return self.suit
def getval(self):
return self.value
def __str__(self):
if(self.suit == 'Clubs'):
suitstr = u'\u2663'
elif(self.suit == 'Diamonds'):
suitstr = u'\u2666'
elif(self.suit == 'Hearts'):
suitstr = u'\u2665'
elif(self.suit == 'Spades'):
suitstr = u'\u2660'
if((self.value<11)&(self.value>1)):
valuestr = str(self.value)
elif(self.value == 11):
valuestr = 'J'
elif(self.value == 12):
valuestr = 'Q'
elif(self.value == 13):
valuestr = 'K'
elif((self.value == 1)|(self.value == 14)):
valuestr = 'A'
return(valuestr+suitstr)
class Deck(object):
def __init__(self,DeckCards=[]):
self.DeckCards = DeckCards
def builddeck(self):
suits = ['Hearts','Diamonds','Clubs','Spades']
for suit in suits:
for i in range(13):
self.DeckCards.append(Card(suit,i+1))
def shuffle(self):
for i in range(len(self)):
r = random.randint(0,len(self)-1)
self.DeckCards[i],self.DeckCards[r] = self.DeckCards[r],self.DeckCards[i]
def draw(self):
return self.DeckCards.pop()
def __str__(self):
return str([card.__str__() for card in self.DeckCards])
def __len__(self):
return len(self.DeckCards)
class Player(object):
def __init__(self,Name,PlayerHandcards = [],Balance = 1000):
self.Name = Name
self.Hand = PlayerHandcards
self.Balance = Balance
def deal(self,deck):
self.Hand.append(deck.draw())
def __str__(self):
return 'Name :'+str(self.Name)+'\n'+'Hand: '+str([card.__str__() for card in self.Hand])+'\n'+'Balance: '+str(self.Balance)
deck1 = Deck()
deck1.builddeck()
deck1.shuffle()
Alice = Player('Alice')
Alice.deal(deck1)
print(Alice)
Bob = Player('Bob')
Bob.deal(deck1)
print(Bob)
And after dealing to Bob they both have the same hands. If you have some other suggestions regarding the code, you are welcome to share that as well.
This is a duplicate of “Least Astonishment” and the Mutable Default Argument as indicated by #Mad Physicist. Closing this question for the same.

Name Error python, a text adventure game

So i have a problem, that i don't quite understand why it's happening. I get a (Name Error global variable "value" is not defined) when it should be on my weapons class.
from items import *
class weapons(Item):
def __init__(self, name, attack_damage, lifesteal = 0):
super(weapons,self).__init__(name, value, quantity=1)
self.attack_damage = attack_damage
self.lifesteal = lifesteal
Here is the class that weapons is getting it from that already has value defined.
class Item(object):
def __init__(self, name, value, quantity=1):
self.name = name
self.raw = name.replace(" ","").lower()
self.quantity = quantity
self.value = value
self.netValue = quantity * value
def recalc(self):
self.netValue = self.quantity * self.value
I already have a piece of code similar to this that is working, but for some reason this value error is happening. I'm just going to include it.
from character import*
class player(character):
def __init__(self,name,hp,maxhp,attack_damage,ability_power):
super(player,self).__init__(name, hp, maxhp)
self.attack_damage = attack_damage
self.ability_power = ability_power
and the class that player is getting its stuff from
class character(object):
def __init__(self,name,hp,maxhp):
self.name = name
self.hp = hp
self.maxhp = maxhp
def attack(self,other):
pass
as you can see i did it here and this piece of code works when i call a player.
You need to add the value argument to the __init__ constructor of the weapons class.
super needs a parameter value but you did not pass it into the init

Resources