Passing a class to another class (Python) - python

I'm having some trouble with classes at the minute, and I not sure of how to solve my problem. I've read the docs and I can't connect anything said there with the problem I'm having.
I'm trying to make some simple classes for a game. I have a Weapon class and a Person class. I'm trying to pass a Weapon to the Person class (I hope this makes sense), so that the Person (Bob) can use the weapon. I'm having trouble accessing the methods and attributes in the Weapon class. I've considered making Person a child class of Weapon so that it can call the method easily, but that doesn't seem intuitive to me . . .
class Weapon:
def __init__(self, weapon_name, weapon_damage):
self.weapon_name = weapon_name
self.weapon_damage = weapon_damage
def display_weapon_name(self):
print('Weapon Name: %s' %self.weapon_name)
class Person:
def __init__(self, person_name, health, ranged_weapon):
self.person_name = person_name
self.health = health
Weapon.ranged_weapon = ranged_weapon
def display_person_info(self):
print('Name: %s' %self.person_name)
print('Ranged Weapon :%s' %Weapon.display_weapon_name)
def ranged_attack(self, ranged_weapon, target):
target.health -=ranged_weapon.weapon_damage
print("Weapon: %s" %ranged_weapon.weapon_name)
print(target.person_name + "'s Health: "+str(target.health))
pistol = Weapon("Pistol", 40)
bob = Person("Bob", 100, pistol)
bob.display_person_info()
Running this gives me:
Name: Bob
Ranged Weapon :<function Weapon.display_weapon_name at 0x02E23030>
Running:
bob.ranged_attack(pistol, bob)
Gives:
Weapon: Pistol
Bob's Health: 60
My questions are, am I passing the Weapon object correctly to the Person class? It seems weird writing Weapon.ranged_weapon in _init__ rather than self.ranged_weapon.
How can I get the display_weapon_info to show the string 'Weapon Name: Pistol', rather than the reference? It seems to work when I call it in ranged_attack, but not in the display info.
Really appreciate any help I can get with this. Apologies if a similar question has been asked before, but I couldn't find anything I could relate to my issue.
Rich

Person doesn't actually need to reference the Weapon class directly; it just needs to save a reference to whatever is passed as the ranged_weapon argument and know what it can do with that object. The code implicitly assumes that ranged_weapon is an instance of Weapon, but will work with any object that is suitably similar to an instant of Weapon.
class Person:
def __init__(self, person_name, health, ranged_weapon):
self.person_name = person_name
self.health = health
self.weapon = ranged_weapon
def display_person_info(self):
print('Name: %s' %self.person_name)
# display_weapon_name already calls print; but
# you probably don't need this method at all.
self.weapon.display_weapon_name()
# Instead, do this (actually, you already do this
# in ranged_attack())
# print('Weapon: %s' % self.weapon.weapon_name)
def ranged_attack(self, ranged_weapon, target):
target.health -= self.weapon.weapon_damage
print("Weapon: %s" % self.weapon.weapon_name)
print(target.person_name + "'s Health: "+str(target.health))

def display_person_info(self):
print('Name: %s' %self.person_name)
print('Ranged Weapon :%s' %Weapon.display_weapon_name)
Looking at this function, the compiler sees the following:
Line 1: A function named display_person_info with the parameter self.
Line 2: Print "Name: " and then print the name of self
Line 3: Print "Ranged Weapon: " and then Weapon.display_weapon_name.
In line 3, the compiler, rather than printing the weapon name, it is printing the function display_weapon_name itself! What you need to do instead is replace Line 3 with this:
print('Ranged Weapon :%s' %self.weapon.display_weapon_name())
That way, the value returned by the function is printed, rather than the function's pointer.

Related

Python dictionary nested within method auto-executes all values (methods) when outer method is called

I'm working on a simple skeleton for a game, and in an effort to try and be more "pythonic", I'm using objects/classes/dictionaries to try and capture all my actions/behaviors (as methods over functions, etc).
For some reason, every time I execute the method 'act' within the class "Player", the dictionary embedded within act runs all of its values (which are, in turn, methods from within the same instance of the class "Player"). In other words, the player chooses "attack, heal, and flee" every time, all at once, before being prompted.
I'm sure there's a simple explanation, but I've been looking for hours and can't find another example of someone's dictionary auto-running all the methods embedded within. Can you help?
Thanks!
- Jake
from random import randint
### BEGIN ALL CLASSES HERE
# To be used for all game objects (living and non-living)
class gameObject(object):
def __init__(self, name):
self.name = name
# To be used for all characters who can act in some way/be killed/change
class livingThing(gameObject):
def __init__(self, name, HP=1):
self.name = name
self.HP = HP
# The playable character(s)
class Player(livingThing):
def __init__(self,name="The Stranger", HP=4, MP=5, strength=1, intellect=1, spirit=1, luck=5, gil=6):
self.name = name
self.HP = HP
self.MP = MP
self.gil = gil
self.strength = strength
self.intellect = intellect
self.spirit = spirit
self.luck = luck
def act(player, enemy):
actions = {
"attack" : player.attack(enemy),
"heal" : player.heal(enemy),
"flee" : player.flee()
}
#Takes input from the player
decision = input("What would you like to do? ")
if decision.lower() in actions:
actions[decision.lower()]
else:
print("That didn't work! Try again.")
# Prints both player and enemy HP
def printHP(player, enemy):
print("{0}'s' HP: {1} \n{2}'s HP: {3}".format(player.name, player.HP, enemy.name, enemy.HP))
# Allows the player to attack an enemy (currently functional)
def attack(player, enemy):
enemy.HP -= player.strength
print("You strike {0} for {1} damage!".format(enemy.name, player.strength))
player.printHP(enemy)
# Allows the player to heal a certain amount of health based on its "spirit" stat (currently functional)
def heal(player, enemy):
healed = randint(0, player.spirit)
player.HP += healed
print("You've healed for {0}!".format(healed))
player.printHP(enemy)
#Allows the player to attempt to run away
def flee(player):
randluck = randint(0, player.luck)
if randluck > 3:
print("You successfully escaped!")
return player.HP
else:
print("You weren't able to escape!")
# Anything that can act with/against the player
class Actor(livingThing):
def __init__(self, name="Unknown Entity", HP=10, MP=2, gil=3):
self. name = name
self.HP = HP
self.MP = MP
self.gil = gil
### END ALL CLASSES ###
### DICTIONARIES CONTAINING ACTIONS ###
### CHARACTERS ###
fighter = Player()
monster = Actor()
fighter.act(monster)
I see the problem. When you are executing Python code, and you have a dictionary as you do, Python evaluates the dictionary fully. If you wanted your values (in your key:value) pairs to be the results of those methods, this is surely one way to do it.
In your case, what you can do is reference the function itself, and not invoke it. You can do this by getting rid of the parentheses, like this:
player.attack
instead of
player.attack()
Then, to call the function you can do something like
actions[decision.lower()](enemy)
Since one of your functions, flee, doesn't accept any parameters, you could give flee a parameter that you simply don't use in the function. If you were designing many many methods that your player can act with, then one strategy would be to give them all only named parameters, like this:
def f1(enemy=None,something=None,foo=None):
if enemy is None:
raise Exception("enemy cannot be None")
#process_enemy
If however, you also have a very high amount of parameters, then you could do this:
def attack(**kwargs):
#kwargs is a dictionary of parameters provided to the function
enemy = kwargs.get('enemy',None)
if enemy is None:
raise Exception("enemy cannot be None")
def eat(**kwargs):
food = kwargs.get('food',None)
if enemy is None:
raise Exception("food cannot be None")
attack(enemy="someenemyobject")
eat(food="somefoodobject")
attack() # raises Exception
attack(food="somefoodobject") # raises Exception
food(enemy="someenemyobject") # raises Exception
food(food="somefoodobject",enemy="someenemyobject") # does not raise Exception

How do you take an input and call an instance of a class based on the input?

I am currently developing a short text-based adventure so I can learn how to use Classes within Python. As part of this, I am trying to create a combat system where the player could choose an NPC to attack.
The aim is that the player can enter the name of the NPC and the weapon they want to use. A method in the target's class will then be called, to lose health based on the damage of the weapon.
My current code is below:
class npc:
def __init__(self, name, alliance):
self.name = name
self.alliance = alliance
def loseHealth(self, health, dmg):
self.dmg = dmg
self.health = self.health - dmg
def usePotion(self, health, pType):
if pType == "great":
self.health = min(self.health + 50,self.maxHealth)
elif pType == "normal":
self.health = min(self.health + 25,self.maxHealth)
else:
pass
def attack(self, target, weaponDmg):
if target in npcList:
target.loseHealth(self.health, weaponDmg)
class human(npc):
maxHealth = 100
health = 100
def __init__(self, name, alliance):
super().__init__(name, alliance)
class orc(npc):
maxHealth = 200
health = 200
def __init(self, name, alliance):
super().__init__(name, alliance)
weaponDmg = {'sword':10,'axe':20}
alice = human("alice","good")
bob = orc("bob","evil")
npcList = [alice, bob]
target = input("Enter Target:")
weapon = input("Enter weapon:")
for x in range(3):
alice.attack(target,weaponDmg[weapon]) #using alice temporarily until I have a person class sorted
print(target.health)
The simple and pythonic answer is to use a dict of NPCs keyed by name, the same way you’re already doing it with weapons:
npcs = {‘alice’: alice, ‘bob’: bob}
target = input("Enter Target:")
weapon = input("Enter weapon:")
for x in range(3):
alice.attack(npcs[target], weaponDmg[weapon])
print(target.health)
And if you want to look up the attacking NPC by user-supplied name as well as the attackee, you can do the same thing there:
npcs[attacker].attack(npcs[target], weaponDmg[weapon])
If you really want to do this inside the attack method you can keep passing in target as a name (string) and do this:
if target in npcs:
npcs[target].loseHealth(self.health, weaponDmg)
... but that probably isn’t a very good design. It means you’re sharing a global variable, and your NPC objects all “know” about that global dict and all the NPCs in it, which doesn’t seem like part of their responsibility.
You can make this a little less repetitive by creating the dict with a comprehension:
npcs = {npc.name: npc for npc in (alice, bob)}
... or by just creating them directly in the dict instead of in variables that you’re probably never going to otherwise use:
npcs = {}
npcs[‘alice’] = human("alice","good")
npcs[‘bob’] = orc("bob","evil")
You can call a method on an instance by using getattr, here is an example:
>>> class Test:
... def my_method(self, arg1, arg2):
... print(arg1, arg2)
...
>>> t = Test()
>>> getattr(t, 'my_method')('foo', 'bar')
foo bar

Is it possible to pass object arguments backwards?

I have two object classes: class Hero(object): and class Alien(object) each with a name parameter that contains the name of the object instance. In each class I have a class method called def die(self): which defines what happens when the Hero is hit or not hit. While I am able to reference the object inside itself, I'm having trouble doing the backwards operation, which is referencing the object that references the method. Sorry if this is confusing, here is what I mean:
#this die and blast function is contained in both classes
def die(self):
#(pseudocode) if hit:
print (self.name, " is hit!")
#(pseudocode) elif not hit:
print (enemy.name, " misses!") #<-- currently doesn't work because enemy is not defined in the die function
def blast(self, enemy):
print(self.name," blasts ", enemy.name, "\n")
enemy.die()
def main():
heroObject = Hero()
alienObject = Alien()
heroObject.blast(alienObject)
Here's my question: I know that when I call blast() that I use the name of the object as the argument. But inside of the blast() and die() functions, how can I call the name of the object that originally called it? The line print (enemy.name, " misses!") doesn't currently work because enemy is not defined in the die function. Ideally what would happen is the name of the enemy would appear in the print statement.
If you need to access the other object from die(), pass it into the function explicitly:
def die(self, enemy):
#(pseudocode) if hit:
print (self.name, " is hit!")
#(pseudocode) elif not hit:
print (enemy.name, " misses!")
def blast(self, enemy):
print(self.name," blasts ", enemy.name, "\n")
enemy.die(self)
def main():
heroObject = Hero()
alienObject = Alien()
heroObject.blast(alienObject)
Notice how the meaning of self and enemy is reversed when we go from blast() into die().
One observation, if I may, is that it seems a bit odd to have the "if misses" logic inside die(). It seems to imply that die() could be called even if the enemy hasn't died and isn't about to die. I think that logic belongs outside the function.

How do I have various pieces of information associated with one value in a dictionary?

EDIT: CHECK AT THE BOTTOM FOR A MORE CLEAR VIEW OF WHAT I AM DOING, PLEASE!
As an example, let's say I have information on three cars:
Car One
500hp
180mph
15mpg
Car Two
380hp
140mph
24mpg
Car Three
450hp
170mph
20mpg
I want to put that in a dictionary, or SOMETHING, so that I can easily access it through a function.
def fuel_eco(car):
return("The fuel economy for %s is %s" % (car, mpg))
def top_speed(car):
return("The top speed for %s is %s" % (car, speed))
def horsepower(car):
return("The horsepower for %s is %s" % (car, hp))
Basically have a module with some functions and a list/dictionary/whatever of the information, and then have another script that asks what car they want to view info on, and what information they want to know.
import carstats
car = input("What car do you want to find out about?")
stat = input("What information do you want to know?")
getStat = getattr (carstats, stat)
print(getStat(car))
How do I store the information for the three vehicles (And more if I add them) in a dictionary, so I can retrieve the information?
Okay, these are the actual files I am working with:
File one is asoiaf.py:
def sigil (house):
"""
Function to return a description of the sigil of a specified Great House.
Takes one argument, the name of the House.
"""
house = house.lower ()
if house == "stark":
sigil = "a grey direwolf on a white field."
elif house == "lannister":
sigil = "a golden lion rampant on a crimson field."
elif house == "targaryen":
sigil = "a red three-headed dragon on a black field."
else:
sigil = "Unknown"
house = str(house[0].upper()) + str(house[1:len(house)])
return("The sigil for House %s is %s" % (house, sigil))
def motto (house):
"""
Function to return the family motto of a specified Great House.
Takes one argument, the name of the House.
"""
house = house.lower ()
if house == "stark":
motto = "Winter is coming!"
elif house == "lannister":
motto = "Hear me roar!"
elif house == "targaryen":
motto = "Fire and blood!"
else:
motto = "Unknown"
house = str(house[0].upper()) + str(house[1:len(house)])
return("The motto for House %s is: %s" % (house, motto))
The second file is encyclopedia.py:
import asoiaf
#import sl4a
#droid = sl4a.Android ()
#sound = input ("Would you like to turn on sound?")
info = "yes"
while info == "yes":
#if sound == "yes":
# droid.ttsSpeak ("What house do you want to learn about?")
house = input ("What house do you want to learn about?")
house = str(house[0].upper()) + str(house[1:len(house)])
#if sound == "yes":
# droid.ttsSpeak ("What do you want to know about House %s?" % house)
area = input ("What do you want to know about House %s?" % house)
getArea = getattr (asoiaf, area)
#if sound == "yes":
# droid.ttsSpeak (getArea (house))
print (getArea (house))
#if sound == "yes":
# droid.ttsSpeak ("Would you like to continue learning?")
info = input ("Would you like to continue learning?")
if info == "no":
print ("Goodbye!")
You'll see a lot of commenting out in the last code, because I had to comment out the TTS that I have for my phone, since most people are not on an Android right now. As you can see, I am using IF, ELIF, ELSE in the functions, and I am just trying to see if there is an easier way. I apologize if it is/was confusing.
Creating a class should be the best way to do it:
class Car: # define the class
def __init__(self, name, speed, hp, mpg):
# This is the constructor. The self parameter is handled by python,
# You have to put it. it represents the object itself
self.name = name
self.speed = speed
self.hp = hp
self.mpg = hp
# This bind the parameters to the object
# So you can access them throught the object
You can then use the object this way:
my_car1 = Car('Car One', 180, 500, 15)
my_car1.speed # Will return 180
Concercing the __init__ name, it has to be this name, all constructors have this name (that's how Python know it is the class constructor). The __init__ method is called when you call Car('car one', 180, 500, 15). You have to ommit the self parameter, Python handle it.
You can add other function to your class, like
def top_speed(self):
return 'The top speed is {}'.format(self.speed)
Then you simply have to do my_car1.topspeed()
In every function you define in a class self must be the first parameter (except some rare cases such as classmethod or staticmethods). Obviously the topseed function works only if you create it in the class Car: block.
I'd suggest you should read more about object oriented programming (OOP) in Python. Just google OOP python and you will have a lot of serious ressources explaining you how to create classes and how to use them.
This official python classes tutorial should help you a lot in understanding the concept.
EDIT:
Regarding the accessing of the class in an other script. It's simple:
let's say you save the code above in a car.py file. Just place that file in the same folder as your other script, and in your other script do:
from car import Car # car is the name of the .py file, Car is the class you want to import
name = input('Car name: ')
speed = int(input('Car speed: ')) # input return a string, you have to cast to an integer to have a number
hp = int(input('Car hp: '))
mpg = int(input('Car mpg : '))
my_car = Car(name,speed,hp,mpg) # Then you just create a Car Object with the data you fetched from a user.
stuff = my_car.speed * my_car.hp # An example of how to use your class
print('The given car have {} mph top speed and have {} horsepower'.format(my_car.speed,my_car.hp))
What you have to understand is that a Class is some kind of a formated data type. When creating a Car class, you are defining how to create a car object. And Each time you call Car(...), you actually create one of these object, the value you put in the object are whatever values you want to put. It could be random number, user input or even network fetched data. You can use this object as you want.
Edit 2:
Given your code. Creating classes will change some things. Let's Give an example.
File 1 houses.py:
class House: # defining a house class
def __init__(self,name, sigil, motto):
self.name = name
self.sigil = sigil
self.moto = motto
# Then, in the same file, you create your houses.
starks = House('starks','grey direwolf on a white field','Winter is coming!')
lannisters = House('lannisters', 'a golden lion rampant on a crimson field', 'Hear me roar!')
# let's skip targaryen, it's the same way...
unknown_house = House('unknown','unknown','unknow')
houses = [starks, lannisters]
def get_house(name):
for house in houses:
if house.name == name:
return house
return unknow_house # if no house match, return unknow
Then in your second file. You just se that:
import houses
house_wanted = input('What house do you want to know about?')
my_house = houses.get_house(house_wanted)
print('this is the house {}; Sigil {} and motto {}'.format(my_house.name, my_house.sigil, my_house.motto))
If you plan on working on biggers set. You should have a look at Enums. That could fit what you want.
If you want to getting a precise attribute, you can do it this way:
import houses
house_wanted = input('What house do you want to know about?')
my_house = houses.get_house(house_wanted)
attr= input('What do you want to know about that house?')
print(getattr(my_house,attr.lower()))
Note this last thing will raise an error if you call for non-existent attr (like foo).
There are many ways to solve the broader problem you describe in the text of your question (the question of how to store multiple pieces of information about an object). Classes maybe one good one. Classes have the advantage of better robustness than dictionaries.
To answer the specific question in the summary/title: "how to have more than one item associated with one key in a dictionary" - use dictionaries as the values, like this:
car_info = {'CarOne': {'power': 500, 'speed': 180, 'mileage': 18},
'CarTwo': {'power': 380, 'spead': 200, 'mileage': 10}
}
print "Car Two has power %d mileage %d" % (car_info['CarTwo']['power'], car_info['CarTwo']['mileage'])
You can see that this is not especially robust by trying to access the 'speed' for "CarTwo". If you look closely you will see that because I made a deliberate typo in the initializer for CarTwo, it does not have a speed at all, it has a spead. Classes will catch this error, dictionaries will not.
This is not a reason not to do it with dictionaries - just something to be aware of when deciding for your particular case.
You could create a class, called car, with whatever attributes you want!
Here's a great tutorial on how to do that: class tutorial
I'm on the road right now, but if you're having trouble, please tell me so that I can write some useful code...

Python: Pokemon battle (classes, functions)

I just started learning python and I am hoping you guys can help me comprehend things a little better. If you have ever played a pokemon game for the gameboy you'll understand more as to what I am trying to do. I started off with a text adventure where you do simple stuff, but now I am at the point of pokemon battling eachother. So this is what I am trying to achieve.
Pokemon battle starts
You attack target
Target loses HP and attacks back
First one to 0 hp loses
Of course all of this is printed out.
This is what I have for the battle so far, I am not sure how accurate I am right now. Just really looking to see how close I am to doing this correctly.
class Pokemon(object):
sName = "pidgy"
nAttack = 5
nHealth = 10
nEvasion = 1
def __init__(self, name, atk, hp, evd):
self.sName = name
self.nAttack = atk
self.nHealth = hp
self.nEvasion = evd
def fight(target, self):
target.nHealth - self.nAttack
def battle():
print "A wild appeared"
#pikachu = Pokemon("Pikafaggot", 18, 80, 21)
pidgy = Pokemon("Pidgy", 18, 80, 21)
pidgy.fight(pikachu)
#pikachu.fight(pidgy)
Full code here: http://pastebin.com/ikmRuE5z
I am also looking for advice on how to manage variables; I seem to be having a grocery list of variables at the top and I assume that is not good practice, where should they go?
If I was to have fight as a instance method (which I'm not sure I would), I would probably code it up something like this:
class Pokemon(object):
def __init__(self,name,hp,damage):
self.name = name #pokemon name
self.hp = hp #hit-points of this particular pokemon
self.damage = damage #amount of damage this pokemon does every attack
def fight(self,other):
if(self.hp > 0):
print("%s did %d damage to %s"%(self.name,self.damage,other.name))
print("%s has %d hp left"%(other.name,other.hp))
other.hp -= self.damage
return other.fight(self) #Now the other pokemon fights back!
else:
print("%s wins! (%d hp left)"%(other.name,other.hp))
return other,self #return a tuple (winner,loser)
pikachu=Pokemon('pikachu', 100, 10)
pidgy=Pokemon('pidgy', 200, 12)
winner,loser = pidgy.fight(pikachu)
Of course, this is somewhat boring since the amount of damage does not depend on type of pokemon and isn't randomized in any way ... but hopefully it illustrates the point.
As for your class structure:
class Foo(object):
attr1=1
attr2=2
def __init__(self,attr1,attr2):
self.attr1 = attr1
self.attr2 = attr2
It doesn't really make sense (to me) to declare the class attributes if you're guaranteed to overwrite them in __init__. Just use instance attributes and you should be fine (i.e.):
class Foo(object):
def __init__(self,attr1,attr2):
self.attr1 = attr1
self.attr2 = attr2v
You don't need the variables up the top. You just need them in the init() method.
The fight method should return a value:
def fight(self, target):
target.nHealth -= self.nAttack
return target
You probably want to also check if someone has lost the battle:
def checkWin(myPoke, target):
# Return 1 if myPoke wins, 0 if target wins, -1 if no winner yet.
winner = -1
if myPoke.nHealth == 0:
winner = 0
elif target.nHealth == 0:
winner = 1
return winner
Hope I helped.
I am only going to comment on a few obvious aspects, because a complete code review is beyond the scope of this site (try codereview.stackexchange.com)
Your fight() method isn't saving the results of the subtraction, so nothing is changed. You would need to do something like this:
def fight(target, self):
target.nHealth -= self.nAttack
# check if target is dead now?
I might even recommend not imposing a modification on your target directly. It may be better if you can call an attack(power) on your target, and let it determine how much damage is done. You can then check if the target is dead yet. Ultimately I would think you would have some "dice" object that would determine the outcomes for you.
As for globals... just stop using them. It is a bad habit to have them unless you really have a good reason. Have functions that return results to the caller, which you then make use of:
def func(foo):
return 'bar'
You can however have a module of constants. These are a bunch of values that don't change for the life of the application. They are merely variables that provide common values. You might create a constants.py and have stuff like:
UP = "up"
DOWN = "down"
DEAD = 0
...
... And in your other modules you do:
from constants import *

Categories

Resources