Understanding Classes and __Call__ Methods Py 2.7 - python

Okay I have been running my forehead into some code and I have determined I am just taking shots in the dark at this point. For some reason getting classes to click has been a real pain in the butt and I'm thinking if I used my own personal code it might make sense. So here is a little script game I wrote up to experiment. (Its pretty rough I'm fairly new): https://github.com/Villagesmithy/Zombies-.git
I feel that to understand what I'm doing wrong I have to have it pointed out to me in my own code. There are plenty of questions similar to mine I'm sure but they were going over my head sadly.
The error I'm getting is this:
Attribute Error: Combat instance has no __Call__ method
And here is the class that I think is the problem:
class Combat:
def battle(self):
print start.player, " has: ", hp, "."
print "The zombie has: ", zombie.zom_health()
time.sleep(1)
if haf == True:
total_damage = hero_attacks * hero_damage
zombie.zom_health() - total_damage
print "You strike the zombie for %d damage!" %(total_damage)
print "The zombie's health is %d" %zombie.zom_health()
return zombie.zom_health()
time.sleep(3)
elif haf == False:
total_damage = zombie.zom_damage()- hero.hero_armor()
if total_damage > 0:
total_damage - hp
return hp
print "A zombie shambles through the baricade and damages you!"
print "You lost %d hp! Your hp is now: %d" %(total_damage, hp)
combat_loop()
time.sleep(3)
elif total_damage <= 0:
print "A zombie lurches at you but misses!"
time.sleep(3)
combat_loop()
else:
z.zom_killed()
def initv(battle):
bat = battle()
hero_init = random.randint(1,20)
zom_init = random.randint(1,20)
if hero_init >= zom_init:
#global haf Ignoring for now
haf = True
print "You attack first!"
bat.battle()
elif hero_init < zom_init:
#global haf
haf = False
print "The zombie attacks!"
bat.battle()
def start(): #The only fucking code that works
global zombies
zombies = random.randint(20,30)
arm = random.sample(armor,1)
wea = random.sample(weapon, 1)
player = raw_input("What is your name?")
print player, ",\n"
print "Your colony is under attack by %s zombies!" %(zombies)
print "Hold them off so the others can escape."
print "You are wearing an ", arm, "and weilding a", wea
time.sleep(3)
def combat_loop():
combat_call = Combat()
while zombies > 0:
combat_call.initv()
if zombies <= 0:
print "You held off the zombies off long enough to escape!"
print "With the way clear you grab your belongings and follow suit."
print "The end!"
sys.exit()
Now if you are saying Gee this kiddo has no clue what he is doing you would be correct! I'm just hoping you guys can assist me in making this click. The whole program might need to be burned at the stake I don't know. Any help you can give would be very helpful.

I'm assuming initv is actually a method of Combat, but you forgot to have it take self as a parameter, giving it a parameter named battle instead.
When you call bat.initv(), it's passing self as battle (self is a name of convention; the first positional parameter of a method is self whatever you decide to call it). So when you do bat = battle() in initv, it's the same thing as doing self(), that is, trying to treat an instance of your class as a callable.
From what I can tell, the real goal is to call the battle method, so the definition and first line of initv should be:
def initv(self):
bat = self.battle()
which passes self under the standard name, then calls the battle method on it. It's a little unclear what the battle method returns (it seems to return None implicitly on two code paths, and whatever zombie.zom_health() returns on the third code path, which has a sleep in it that never happens thanks to the return preempting it), but this code has a number of issues, and it's quite hard to identify what "correct" behavior would be.
For the record, the error almost certainly complained about the lack of a __call__ method, not __Call__; the special method that lets instances of a class act as callables themselves is all lower case.

In combat_loop you define combat_call as Combat(). Right now combat_call is an instance of Combat. Then in your while loop, you say combat_call.initv(). Since combat_call is an instance of Combat, that is a shortcut for Combat.initv(combat_call). That is, combat_call is the only argument given to initv(). In initv(), you take one argument: battle. In this case, battle is the same thing as combat_call which is an instance of Combat. You then say bat = battle(). battle is already an instance of Combat, so you are trying to call an instance. Some instances can be called, but to do that, they need to define a __call__ method. Your class did not, so there is an error. I think that instead of taking battle as an argument, take self. Then define bat as self.battle().

One more thing, I don't think the zombie.zom_health() - total_damage line will work unless you implement the __sub__ method in your Zombie class. Even then I think it would only work if Zombie and Hero each had the same parent class say: 'Human'. But I hope not. 'Someone' would have to test that...I need some sleep first before I test more fixes for your Hero class. :) Perhaps Zombie - Hero might work. This example re-worked from your Zombie class works fine when the objects (ie Z1 and Z2) are both from the Zombie class. So...Hero.total_damage() might work just as well. I don't know for sure yet.
import random
class Zombie:
def __init__(self, zom_health=None):
self.__zom_health = None
if zom_health:
self.__zom_health = zom_health
else:
self.randomize_zom_health()
self.__zom_damage = 0
def randomize_zom_health(self):
zom_hp = random.randint(20,35)
if zom_hp <= 0:
print zom_killed
self.__zom_health = 0
else:
self.__zom_health = zom_hp
def __sub__(self, Other):
self.__zom_health -= Other.zom_damage() #or Other.total_damage()?
def zom_health(self):
return self.__zom_health
def zom_damage(self, damage=None):
if damage: self.__zom_damage = damage
return self.__zom_damage
>>> reload(Zombie)
>>> z1 = Zombie.Zombie(20)
>>> z2 = Zombie.Zombie(zom_health=30)
>>> z1.zom_health()
20
>>> z2.zom_health()
30
>>> z2.zom_damage(z2.zom_health())
30
>>> z1 - z2
>>> z1.zom_health()
-10
>>>

Related

Global variables not carrying across FUNCTIONS

(For those who saw this question the last time I asked it, I sincerely apologize, I used the term "module" when I meant "function", but thank you for your very helpful advice nontheless! I'll make sure to keep it in mind when I begin to add other files into the equation.)
I'm trying to make a text based adventure game using python, and as a result it requires a lot of variables, and as backtracking is a must, I need to use global variables for the essential ones. I have run into speed bumps when trying to get these to be read by other functions. This is the line of code used to define the universal variables, and their starting value
def reset():
global gold, exp, etnl, maxHP, curHP, maxmana, curmana, attack, defence, helm, armtop, armbot, boots, gloves, weapons
gold = 0
exp = 0
etnl = 100 #exp to next level
maxHP = 50
curHP = 50
maxmana = 10
curmana = 10
attack = 5
defence = 5
helm = "none"
armtop = "none"
armbot = "none"
boots = "none"
gloves = "none"
weapon = "fists"
And for example, when I try to display one of the global variables, it shows up as the variable being undefined, as shown here:
def gamestart():
clear() #this command is fine, simply to make it look neater when it is run again
print("you wake up in a clearing in the forest, you can't remember what happened.")
print("you feel numb, you realize you're lying flat on your back.")
print
print("HP " + str(curHP) + "/" + str(maxHP))
Can someone help me out with this?
Is there an easier way to do this?
All help is appreciated!
(yes, I make sure to run the reset function before the newgame function)
A much simpler version if this, at least according to me is:
def variable():
global foo
foo = 7
def trigger():
variable():
output():
def output():
print(foo)
You could store those things into a class used as storage-container. If you declare them classvariables and any accessors as #classmethods you do not need an instance.
class GameState:
gold = 0
exp = 0
etnl = 100 #exp to next level
maxHP = 50
curHP = 50
maxmana = 10
curmana = 10
helm = "none"
armtop = "none"
armbot = "none"
boots = "none"
gloves = "none"
weapon = "fists"
weapons = {"fists":(5,5),"sword":(15,12),"mace":(30,3),"cushion":(2,20)}
#classmethod
def reset(cls):
cls.gold = 0
cls.exp = 0
cls.etnl = 100 #exp to next level
cls.maxHP = 50
cls.curHP = 50
cls.maxmana = 10
cls.curmana = 10
cls.helm = "none"
cls.armtop = "none"
cls.armbot = "none"
cls.boots = "none"
cls.gloves = "none"
cls.weapon = "fists"
#classmethod
def attack(cls):
return cls.weapons.get(cls.weapon,(0,0))[0]
#classmethod
def defense(cls):
return cls.weapons.get(cls.weapon,(0,0))[1]
for w in State.weapons:
State.weapon = w
print("{} has attack {} and defense {}.".format(w, State.attack(),State.defense()))
Output:
fists has attack 5 and defense 5.
sword has attack 15 and defense 12.
mace has attack 30 and defense 3.
cushion has attack 2 and defense 20.
You might want to seperate some things out - f.e. an extra class for the weapon/damage/defense related stuff ...
More reading:
What is the difference between #staticmethod and #classmethod?
https://docs.python.org/3/tutorial/classes.html#class-and-instance-variables
Instead of global variables have you considered storing all the stats in a class/struct? Create an instance of the class at the start of the game, with its default values being specified in the constructor.
G = StartClass()
def gamestart():
print("you wake up in a clearing in the forest, you can't remember what happened.")
print("you feel numb, you realize you're lying flat on your back.")
print("HP " + str(G.curHP) + "/" + str(G.maxHP))
Alternatively, declaring G globally and passing it into gamestart(G) and/or re-instantiating in the reset() function might be options.
Here is a simple example of what I think you are trying to accomplish. If you are using global variables, then you need to be sure you are not inadvertently creating local variables with the same names in your functions (when you mean to be modifying the global variable).
You should look at using classes which I think would help you with some of the semantic confusion here.
value = 10
def reset():
global value
value = 10
def start():
print(f'initial value: {value}')
global value
value += 1
print(f'updated value: {value}')
reset()
print(f'reset value: {value}')
start()
# OUTPUT
# initial value: 10
# updated value: 11
# reset value: 10

Making a combat system using classes

I've been trying to implement a combat system in my text game. I thought I could just create instances in each of my scenes that have different int values to minimize hard-coding that the exercise from the book I have I think is trying to teach me. When I access the TestRoom class by dict it says in Powershell:
TypeError: __init__() takes exactly 4 arguments (1 given)
Please help me figure out how to do this I'm new to Python and the book doesn't explain classes that well.
class Hero(object):
def __init__(self, health1, damage1, bullets1):
self.health1 = health1
self.damage1 = damage1
self.bullets1 = bullets1
class Gothon(object):
def __init__(self, health2, damage2):
self.health2 = health2
self.damage2 = damage2
class Combat(object):
def battle():
while self.health1 != 0 or self.health2 != 0 or self.bullets1 != 0:
print "You have %r health left" % self.health1
print "Gothon has %r health left" % self.health2
fight = raw_input("Attack? Y/N?\n> ")
if fight == "Y" or fight == "y":
self.health2 - self.damage1
self.health1 - self.damage2
elif fight == "N" or fight == "n":
self.health1 - self.damage2
else:
print "DOES NOT COMPUTE"
if self.health1 == 0:
return 'death'
else:
pass
class TestRoom(Combat, Hero, Gothon):
def enter():
hero = Hero(10, 2, 10)
gothon = Gothon(5, 1)
This is in Python 2.7
Side note: Is learning Pyhton 2.7 a bad thing with Python 3 out? Answer isn't really required, just wondering.
I'm not sure why you've made TestRoom inherit from those other three classes. That doesn't make sense; inheritance means an "is-a" relationship, but while a room might contain those things, it isn't actually any of those things itself.
This is the source of your problem, as TestRoom has now inherited the __init__ method from the first one of those that defines it, Hero, and so it requires the parameters that Hero does. Remove that inheritance.

Python AttributeError: type object 'x' has no attribute 'x'

I'm currently working on a simple text-based game in Python just to practice python and object-oriented programming but I'm running into this error where it tells me that 'LargeManaPotion' has no attribute 'name' when I can see that it does, and it is declared the exact same way as 'SmallManaPotion' which works just fine. I'm assuming it's some stupid mistake that I'm just overlooking or something but would appreciate the help. Also, the program will print the potion just fine when I print the inventory of the player in the player.inventory function so I'm not sure why it doesn't work with within the trade function. Anyways, here is the relevant code. Thanks in advance.
class ManaPotion:
def __init__(self):
raise NotImplementedError("Do not create raw ManaPotion objects.")
def __str__(self):
return "{} (+{} Mana)".format(self.name, self.mana_value)
class LargeManaPotion(ManaPotion):
def __init__(self):
self.name = "Large Mana Potion"
self.mana_value = 45
self.value = 40
class SmallManaPotion(ManaPotion):
def __init__(self):
self.name = "Small Mana Potion"
self.mana_value = 15
self.value = 10
As you can see it is identical to the SmallManaPotion.
Here is the function which causes the error.
class TraderTile(MapTile):
def intro_text(self):
return "A frail not-quite-human, not-quite-creature squats in the corner " \
"\nclinking his gold coins together. \nHe looks willing to trade."
def __init__(self, x, y):
self.trader = npc.Trader()
super().__init__(x, y)
def trade(self, buyer, seller):
for i, item in enumerate(seller.inventory, 1):
#the line below here is where I'm getting the error.
print("{}. {} - {} Gold".format(i, item.name, item.value))
while True:
user_input = input("Choose an item or press Q to exit: ")
if user_input in ['q', 'Q']:
return
else:
try:
choice = int(user_input)
to_swap = seller.inventory[choice - 1]
self.swap(seller, buyer, to_swap)
except ValueError:
print("Invalid choice!")
def swap(self, seller, buyer, item):
if item.value > buyer.gold:
print("That's too expensive.")
return
seller.inventory.remove(item)
buyer.inventory.append(item)
seller.gold = seller.gold + item.value
buyer.gold = buyer.gold - item.value
print("Trade complete!")
def check_if_trade(self, player):
while True:
print("\n\nGold: {} \nWould you like to (B)uy, (S)ell, or (Q)uit?".format(player.gold))
user_input = input()
if user_input in ['Q', 'q']:
return
elif user_input in ['B', 'b']:
print("\n\nGold: {} \nHere's whats available to buy: ".format(player.gold))
self.trade(buyer=player, seller=self.trader)
elif user_input in ['S', 's']:
print("\n\nGold: {} \nHere's what's available to sell: ".format(player.gold))
self.trade(buyer=self.trader, seller=player)
else:
print("Invalid choice!")
However, this function calls LargeManaPotion but without any errors.
def print_inventory(self):
print("Inventory:")
for item in self.inventory:
print('* ' + str(item))
print("* Gold: {}".format(self.gold))
best_weapon = self.most_powerful_weapon()
print("Your best weapon is your {}".format(best_weapon))
Error and stacktrace:
Choose an action:
i: Print inventory
t: Trade
n: Go north
s: Go south
w: Go west
m: Replenish Mana
Action: t
Gold: 33
Would you like to (B)uy, (S)ell, or (Q)uit?
>>>b
Gold: 33
Here's whats available to buy:
1. Crusty Bread - 12 Gold
2. Crusty Bread - 12 Gold
3. Crusty Bread - 12 Gold
4. Healing Potion - 60 Gold
5. Healing Potion - 60 Gold
6. Small Mana Potion - 10 Gold
7. Small Mana Potion - 10 Gold
Traceback (most recent call last):
File "/Users/Cpt_Chirp/Documents/Escape/game.py", line 74, in <module>
play()
File "/Users/Cpt_Chirp/Documents/Escape/game.py", line 17, in play
choose_action(room, player)
File "/Users/Cpt_Chirp/Documents/Escape/game.py", line 30, in choose_action
action()
File "/Users/Cpt_Chirp/Documents/Escape/player.py", line 112, in trade
room.check_if_trade(self)
File "/Users/Cpt_Chirp/Documents/Escape/world.py", line 127, in check_if_trade
self.trade(buyer=player, seller=self.trader)
File "/Users/Cpt_Chirp/Documents/Escape/world.py", line 96, in trade
print("{}. {} - {} Gold".format(i, item.name, item.value))
AttributeError: type object 'LargeManaPotion' has no attribute 'name'
Process finished with exit code 1
I don't believe you have provided the correct code, but you have provided enough to determine what's going on here
a = list()
b = list
a.append(1)
b.append(1)
Which one of these will raise an error? Obviously, the append to b. While objects of type "list" have a method "append", the base class "Type List" does not.
Somewhere, you have assigned the type LargeManaPotion to a variable and attempted to access the field name from it. But the type itself does not have those fields. The reason you can do this is because in python, classes are first class objects and can be passed around like any other objects
Let's looking at something closer to your live code
class Pot(object):
def add(self):pass
pots = [Pot(), Pot(), Pot(), Pot(), Pot]
for pot in pots: pots.add()
Now where is the problem? They are all instances of Pot, are they not? Why does only the last one raise an AttributeError?
Of course, because they are not all the same. The first 4 items are instances of the class Pot. The are returned from the method __new__, defined in the class type Pot which is invoked when I use that "parentheses notation" after the variable name. At runtime, python has no idea what the variable "Pot" is. It happens to be a type variable, who's invocation generates an instance object.
The last item is an instance of the class "type Pot". It is not a Pot. It is a type. It's __class__ attribute is not Pot. It's __class__ attribute is type Types are used to generate instances. it is meaningless to "add" to a type.
Let's say you had potions in real life. You can do things with potions. You can drink them. You can check their boiling points (if they have a tag, or maybe through science).
Instead, let's say you had the recipe for a potion lying around. And you said: "drink the recipe". "What's the recipe's boiling point". The universe is responding: "that's undefined". You meant to look at a potion. Instead you looked at its recipe. Like all OO metaphors, this one is incomplete. Additional reading:
What happens when I use () notation on a class
What happens if I use () notation on a nonclass
Class attributes?
Wait what's a class
Wait what's a type
Wait what's a metaclass
Wait what's a metaphor
Wait what's meta
On the line you indicate an error with a comment, try instead calling
print(item)
I think you may see some surprising results. At a prompt, I did the following:
>>> print(LargeManaPotion)
<class '__main__.LargeManaPotion'>
>>> print(LargeManaPotion())
Large Mana Potion (+45 Mana)
You didn't seem to provide the full source for your program (thus I can't verify), but I suspect you're seeing the "<class ..." line. Since I have to guess, I'd say the place you construct the seller's inventory is referring to the class itself
LargeManaPotion
rather than instantiating (calling) it
LargeManaPotion()

How do I pass variables around in Python?

I want to make a text-based fighting game, but in order to do so I need to use several functions and pass values around such as damage, weapons, and health.
Please allow this code to be able to pass "weapons" "damage" "p1 n p2" throughout my code. As you can see I have tried using parameters for p1 n p2, but I am a little bit a newbie.
import random
def main():
print("Welcome to fight club!\nYou will be fighting next!\nMake sure you have two people ready to play!")
p1=input("\nEnter player 1's name ")
p2=input("Enter player 2's name ")
print("Time to get your weapons for round one!\n")
round1(p1,p2)
def randomweapons(p1,p2):
weapon=["Stick","Baseball bat","Golf club","Cricket bat","Knife",]
p1weapon=random.choice(weapon)
p2weapon=random.choice(weapon)
print(p1 +" has found a "+p1weapon)
print(p2 +" has found a "+p2weapon)
def randomdamage():
damage=["17","13","10","18","15"]
p1damage=random.choice(damage)
p2damage=random.choice(damage)
def round1(p1,p2):
randomweapons(p1,p2)
def round2():
pass
def round3():
pass
def weaponlocation():
pass
main()
There are a few options.
One is to pass the values as parameters and return values from your various functions. You're already doing this with the names of the two players, which are passed as parameters from main to round1 and from there on to randomweapons. You just need to decide what else needs to be passed around.
When the information needs to flow the other direction (from a called function back to the caller), use return. For instance, you might have randomweapons return the weapons it chose to whatever function calls it (with return p1weapon, p2weapon). You could then save the weapons in the calling function by assigning the function's return value to a variable or multiple variables, using Python's tuple-unpacking syntax: w1, w2 = randomweapons(p1, p2). The calling function could do whatever it wants with those variables from then on (including passing them to other functions).
Another, probably better approach is to use object oriented programming. If your functions are methods defined in some class (e.g. MyGame), you can save various pieces of data as attributes on an instance of the class. The methods get the instance passed in automatically as the first parameter, which is conventionally named self. Here's a somewhat crude example of what that could be like:
class MyGame: # define the class
def play(self): # each method gets an instance passed as "self"
self.p1 = input("Enter player 1's name ") # attributes can be assigned on self
self.p2 = input("Enter player 2's name ")
self.round1()
self.round2()
def random_weapons(self):
weapons = ["Stick", "Baseball bat", "Golf club", "Cricket bat", "Knife"]
self.w1 = random.choice(weapons)
self.w2 = random.choice(weapons)
print(self.p1 + " has found a " + self.w1) # and looked up again in other methods
print(self.p2 + " has found a " + self.w2)
def round1(self):
print("Lets pick weapons for Round 1")
self.random_weapons()
def round2(self):
print("Lets pick weapons for Round 2")
self.random_weapons()
def main():
game = MyGame() # create the instance
game.play() # call the play() method on it, to actually start the game

local variable 'door_open' referenced before assignment

I am very new to python. I am trying to learn by making a text based game.
In the gameplay I am wanting a variable, door_open, to begin false but once unlocked door_open = True. The next time the function is called a different message would be displayed.
door_open =False
def house():
print "The door is locked"
plan = raw_input(">")
if plan.lower() == 'use key':
inventory.remove("House Key")
door_open = True
print "You are inside"
start() ## this just returns to the starting point outside
if door_open == True:
print "You walk inside"
exit(0) ## using this until I develop it more
else:
print "Door is locked. Do you want to use your key (Y/N)?"
When Python sees an assignment to a variable in a function, when the variable is not marked as global (or nonlocal), it treats the variable as a new local variable.
Thus Python is treating the original code similarly to
door_open = False
def house():
if plan.lower() == 'use key':
door_open_LOCAL = True # only assigned here
if door_open_LOCAL == True: # whoops, might not be assigned yet!
pass
(That is, the local variable shadows the global variable, I've emphasized the point above by giving the local variable a different name entirely.)
See also:
Why some Python variables stay global, while some require definition as global
Use of "global" keyword in Python
If you want to write to a variable in the global scope you need to use the global keyword:
door_open = False
def house():
global door_open
print "The door is locked"
# ...
Also, please read and follow PEP8, the Python style guide. For example. you shouldn't use if foo == True: but if foo:
You are trying to track status of your game and got a bit lost in where is the status noted:
This is typical situation for using a class:
game.py:
import sys
class Game():
def __init__(self):
self.door_open = False
self.inventory = ["House Key", "Snow Gun", "Swiss army knife"]
def unlock_house(self):
assert "House Key" in self.inventory, "`House Key` must be present in inventory"
self.inventory.remove("House Key")
print "Click, Click"
self.door_open = True
def fireup(self):
print "Bufff"
self.inventory = []
def main():
game = Game()
while True:
# report current status
if game.door_open:
print "You are in the house!"
print "You are the King"
sys.exit(0)
else:
print "The door is locked!"
# ask for further plans
print "What is your further plan?"
plan = raw_input(">")
# change the situation
if plan.lower() == "use key":
game.unlock_house()
elif plan.lower() == "set up file":
game.fireup()
if __name__ == "__main__":
main()
You can see the split between the class Game, which is able to track the status and even do some changes of it, and the main piece of code, where you try to use the game somehow.
The class Game is sort of idea, how your notes about game status could look like.
Then in game = Game() you instantiate the class, get an object, and start doing real changes to the status.
Having status information stored in well designed and quite well isolated object is very convenient, you know, where to find what you need.

Categories