Is it possible to pass object arguments backwards? - python

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.

Related

How to call a variable from one function into another function?

Note: this is a very long question.
I'm practicing python by making a game. I need to take a variable from another function (that I called spawn()) and use in a different function (that I called damage_taken()).
This is the file enemy.py. Its main job is to spawn an enemy:
import random
import player
class Enemy(object):
def types(self):
type = ["slime", "ghost", "demon"]
enemy = random.choice(type)
return enemy
class Slime(Enemy):
def types(self):
colour = ["red", "green", "blue"]
type = random.choice(colour)
return type
def health(self):
health = random.randint(1,5)
return health
class Ghost(Enemy):
def types(self):
form = ["spirit", "spectre", "phantom"]
type = random.choice(form)
return type
def health(self):
health = random.randint(10,30)
return health
class Demon(Enemy):
def types(self):
being = ["demon", "hell hound", "wendigo"]
type = random.choice(being)
return type
def health(self):
health = random.randint(15,35)
return health
This is the important code. I need to take the variable health from this function and use it in another function.
def spawn():
enemy = Enemy()
bad = enemy.types()
if bad == "slime":
slime = Slime()
target = slime.types()
health = slime.health()
print(f"A {target} {bad} has appeared. It has {health} HP")
return health
elif bad == "ghost":
ghost = Ghost()
target = ghost.types()
health = ghost.health()
print(f"A {target} has appeared. It has {health} HP")
return health
elif bad == "demon":
demon = Demon()
target = demon.types()
health = demon.health()
print(f"A {target} has appeared. It has {health} HP")
return health
This is where i am struggling. I am trying to take the variable health from the function spawn() and use it in the following function. However, it keeps on telling me health does not exist. How do i take a variable from another function and use it in this function.
def damage_taken():
spawn()
health = spawn.health - player.fight()
return health
damage_taken()
The code
spawn.health
is my failed attempt at trying to call the variable into the function.
The code:
player.fight()
is calling a function from a different file called player.py. Its main purpose is to deal with mechanics related to the player such as creating the character and deciding how much damage they deal.
If I well understood your question you just need to assign the return value from your spawn function into a variable:
def damage_taken():
spawn_health = spawn()
health = spawn_health - player.fight()
return health
I'm guessing that you would actually rather the spawn function, return the enemy object, rather than the health of the enemy, so I would recommend the following:
def spawn():
enemy = Enemy()
bad = enemy.types()
if bad == "slime":
enemy = Slime() # each of these now is assigned to the same variable name, "enemy"
target = slime.types()
health = slime.health()
print(f"A {target} {bad} has appeared. It has {health} HP")
elif bad == "ghost":
enemy = Ghost()
target = ghost.types()
health = ghost.health()
print(f"A {target} has appeared. It has {health} HP")
elif bad == "demon":
enemy = Demon()
target = demon.types()
health = demon.health()
print(f"A {target} has appeared. It has {health} HP")
return enemy
def damage_taken():
enemy = spawn()
health = enemy.health - player.fight()
return health
damage_taken()
now, there is still the problem that the enemy object will be entirely dropped after damage_taken() is called. If you want the enemy to persist, you likely will want to initialize both player and enemy outside of the scope of the damage_taken function. Something more like this:
enemy = spawn()
damage_taken(enemy)

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

Calling a class function in an IF statment, keeps returning True

I have a class Player() with a function called use_potion(). When I am using use_potion() in an IF statement, at first it works fine. When the return value for use_potion changes, the if statement ignores the change!
Here's a section of the code:
class Player():
def __init__(self):
inventory = ["potion"]
def has_potion(self):
return any(item == "Potion" for item in self.inventory):
In another module:
from Player import Player
def available_actions():
moves = ["go east","go west"]
if Player().has_potion():
moves.append("use potion")
return moves
When I call available_actions(), it returns all three moves as it is supposed to. But when "potion" is removed from the Player().inventory, available_actions STILL returns all three moves instead of only "go east" and "go west".
I have no idea why this is happening.
You are instantiating a new Player every time you call available_actions. Because the Player class comes with a potion, it will always return True.
Also, you need to save inventory to self in your init function.
You should instantiate the player outside of the function and then pass it as a parameter.
from Player import Player
my_player = Player()
def available_actions(player):
moves = ["go east","go west"]
if player.has_potion():
moves.append("use potion")
return moves
available_actions(my_player)
and in the Player.py file
class Player():
def __init__(self):
self.inventory = ["potion"]
def has_potion(self):
return 'potion' in self.inventory

Passing a class to another class (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.

Get the return value from a function in a class in Python

I am trying to simply get the value out of my class using a simple function with a return value, I'm sure its a trivial error, but im pretty new to python
I have a simply class set up like this:
class score():
#initialize the score info
def __init__(self):
self.score = 0
self.num_enemies = 5
self.num_lives = 3
# Score Info
def setScore(num):
self.score = num
# Enemy Info
def getEnemies():
return self.num_enemies
# Lives Info
def getLives():
return self.getLives
etc.....
Than I create an instance of the class as such:
scoreObj = score()
for enemies in range(0, scoreObj.getEnemies):
enemy_sprite.add(enemy())
I get the error saying that an integer is expected, but it got an instancemethod
What is the correct way to get this information?
Thanks!
scoreObj.getEnemies is a reference to the method. If you want to call it you need parentheses: scoreObj.getEnemies().
You should think about why you are using a method for this instead of just reading self.num_enemies directly. There is no need for trivial getter/setter methods like this in Python.
The first parameter for a member function in python is a reference back to the Object.
Traditionally you call it "self", but no matter what you call the first parameter, it refers back to the "self" object:
Anytime I get weird errors about the type of a parameter in python, I check to see if I forgot the self param. Been bit by this bug a few times.
class score():
#initialize the score info
def __init__(self):
self.score = 0
self.num_enemies = 5
self.num_lives = 3
# Score Info
def setScore(self, num):
self.score = num
# Enemy Info
def getEnemies(self):
return self.num_enemies
# Lives Info
def getLives(foo): #foo is still the same object as self!!
return foo.num_lives
#Works but don't do this because it is confusing
This code works:
class score():
def __init__(self):
self.score = 0
self.num_enemies = 5
self.num_lives = 3
def setScore(self, num):
self.score = num
def getEnemies(self):
return self.num_enemies
def getLives(self):
return self.getLives
scoreObj = score()
for enemy_num in range(0, scoreObj.getEnemies()):
print enemy_num
# I don't know what enemy_sprite is, but
# I commented it out and just print the enemy_num result.
# enemy_sprite.add(enemy())
Lesson Learned:
Class functions must always take one parameter, self.
That's because when you call a function within the class, you always call it with the class name as the calling object, such as:
scoreObj = score()
scoreObj.getEnemies()
Where x is the class object, which will be passed to getEnemies() as the root object, meaning the first parameter sent to the class.
Secondly, when calling functions within a class (or at all), always end with () since that's the definition of calling something in Python.
Then, ask yourself, "Why am I not fetching 'scoreObj.num_lives' just like so instead? Am I saving processing power?" Do as you choose, but it would go faster if you get the values directly from the class object, unless you want to calculate stuff at the same time. Then your logic makes perfect sense!
You made a simple mistake:
scoreObj.getEnemies()
getEnemies is a function, so call it like any other function scoreObj.getEnemies()

Categories

Resources