Referencing self variables outside of original class - python

class player(object):
def __init__(self):
self.health = 100
self.stats = {'STR': 0,
'DEF':0}
class create_character(object):
def choose_class(self):
inp = input("Please choose a class!: ").lower()
s = ("Knight, Archer, or Mage")
print(s)
if inp == 'knight':
self.stats['STR'] = 15
self.stats['DEF'] = 15
print(self.stats)
The problem I'm facing currently, is that when I reference self.stats under create_character, it tells me that specific class doesn't have a 'self.stats'. If I try player.self.stats or player.stats, it tells me the same thing, that the class doesn't have those attributes. How can I reference a dictionary or a variable from another class, and change its properties. Also, when I print self.health, or self.stats, or self.name or whatever properties the player class hold, it gives me a bunch of unneeded information. Is there a way to exclude the extra information?

I believe that this will do the job that you are looking for:
class player(object):
def __init__(self):
self.health = 100
self.stats = {'STR': 0,
'DEF':0}
def choose_class():
inp = input("Please choose a class (Knight, Archer, or Mage): ").lower()
if inp == 'knight':
# create the player object
p = player()
# changes the values for the attributes in the player object
p.stats['STR'] = 15
p.stats['DEF'] = 15
print(p.stats)
# calls the function to choose the class
choose_class()
This is not really a good coding style though, so I would recommend following an OOP Python tutorial.

Related

Video game save/load problems with python pickle (not loading instances)

I'm currently working on a text adventure in python (this language just because it's the one I know), and I'm finding that creating and loading savefiles removes some of the mecahnics I've made. I'll include the problematic code here, rather than all of elements that work fine. it's mainly to do with classes and how instances are 'pickled'.
Here are some of the classes I've created:
class Player:
def __init__(self, name):
self.sack = []
self.kit = []
self.equipped = []
self.log = []
self.done = []
class Weapon:
def __init__(self, name, price, minattack, maxattack):
self.name = name
self.price = price
self.minattack = minattack
self.maxattack = maxattack
class Food:
def __init__(self, name, price, healthadd):
self.name = name
self.price = price
self.healthadd = healthadd
class Quest:
def __init__(self, name, requirement, num, gold, npc, place, give_text, prog_text, comp_text, done_text):
self.name = name
self.requirement = requirement
self.num = num
self.score = 0
self.gold = gold
self.npc = npc
self.place = place
self.give_text = give_text
self.prog_text = prog_text
self.comp_text = comp_text
self.done_text = done_text
The instances in the Player class I've included here are just the ones that are appended by other mechanics with Weapons, Food and Quests. The code includes regions where Weapons, Food and Quests are populated (though working on a separate assets file might tidy things up a bit).
Here's how the save/load functions work currently:
def save(lastplace):
clear()
with open('savefile', 'wb') as f:
PlayerID.currentplace = lastplace.name
pickle.dump(PlayerID, f)
print("Game saved:\n")
print(PlayerID.name)
print("(Level %i)" % (PlayerID.level))
print(lastplace.name)
print('')
option = input(">>> ")
goto(lastplace)
def load():
clear()
if os.path.exists("savefile") == True:
with open('savefile', 'rb') as f:
global PlayerID
PlayerID = pickle.load(f)
savedplace = PlayerID.currentplace
savedplace = locations[savedplace]
goto(savedplace)
else:
print("You have no save file for this game.")
option = input('>>> ')
main()
It's worth noting that upon entry to the game, PlayerID (you) becomes a global variable. You might begin to see some of the issues here, or rather the overarching issue. Essentially, the pickling process serialises all of the possible class types stored in lists within the class of Player just get appended by their names, thus removing their class properties when loaded back into the game.
Is there a pythonic way to ensure that class instances are saved for a future load so that they can still behave as classes, particularly when stacked inside the class of Player? I appreciate this is more of an editorial rather than a question by its length, but any help would be hugely appreciated.

Python/PyGame Creating Random Monsters with Random Attributes from Class/Method

I am attempting to create a text based RPG. I want to use a Class Monsters to create a random monster of a particular type. However I cannot seem to access the random variables associated with the method in the class. Here is a trimmed down version of the code:
import random
class Monsters():
def wolfEnemy(self):
self.hp = random.randint(10, 20)
self.attk = random.randint(1, 3)
self.gold = random.randint(2, 5)
self.xp = 100
monsters = Monsters()
print(monsters.wolfEnemy)
things = monsters.wolfEnemy.hp()
print(things)
I'm not sure how to access variables from within a instantiated method. print(monsters.wolfEnemy) just produces None and things = monsters.wolfEnemy.hp() errors with builtins.AttributeError: 'function' object has no attribute 'hp'. Is there a way to call up that wolfEnemy and the attributes outside the Class/Method.
Define a WolfEnemy class that inherits from the Monster class. In the Monster class you can define attributes and methods that every subclass should have, and override them to create the specific subclasses.
import random
class Monster:
def __init__(self):
self.hp = random.randint(10, 20)
self.attk = random.randint(1, 3)
self.gold = random.randint(2, 5)
self.xp = 100
class WolfEnemy(Monster):
def __init__(self):
# Call the __init__ method of the parent class, that means the
# wolf instance will get the attributes that we've defined there.
super().__init__()
# To override specific attributes, assign new values here.
self.hp = random.randint(20, 30)
wolf = WolfEnemy()
print(wolf.hp)
you can get the result you want like this:
import random
class Monsters():
def wolfEnemy(self):
self.hp = random.randint(10, 20)
self.attk = random.randint(1, 3)
self.gold = random.randint(2, 5)
self.xp = 100
monsters = Monsters()
# wolfEnemy() for the function call not wolfEnemy
monsters.wolfEnemy()
# monsters.hp get the attr not monsters.wolfEnemy.hp()
print(monsters.hp)

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

Variables dont update to new values between classes

I am making a basic RPG style game. I have made different classes for the various parts of the code, one for each of the main items involved (hero, door, monsters etc.)
For both the hero and door, i assign them random locations, shown below in the code, but for the door I run a while loop which makes sure that the door is a certain distance from the hero (using pythagorus).
However the while loop in the door class won't work as it always uses a value of 0 for both heroC and heroR (row and column of the hero). I am relatively new to using classes, but it doesnt seem to make sense as in HeroLocation I assign a random integer to these variables, and HeroLocation is called before DoorLocation.
Any help would be greatly appreciated!!
class Hero(Character):
def __init__(self):
super(Hero, self).__init__(10, 10, 1, 1, 0, 1)
self.herolocations = list(range(1,6)) + list(range(10,14))
self.heroC = 0
self.heroR = 0
def HeroLocation(self):
#place hero
self.heroC = random.choice(self.herolocations)
self.heroR = random.choice(self.herolocations)
class Door:
def __init__(self):
self.hero = Hero()
self.doorC = 0
self.doorR = 0
def DoorLocation(self):
while ((self.hero.heroC-self.doorC)**2+(self.hero.heroR-self.doorR)**2) <= 128:
self.doorC = random.randint(1, 13)
self.doorR = random.randint(1, 13)
class game:
def __init__(self, parent):
self.hero = Hero()
self.door = Door()
def MakeMap(self):
self.hero.HeroLocation()
self.herol = self.Main_game.create_image(15+30*self.hero.heroC,15+30*self.hero.heroR, image = self.heroimage)
self.door.DoorLocation()
self.doorl = self.Main_game.create_image(15+30*self.door.doorC,15+30*self.door.doorR, image = self.exitdoor)
NB there is a lot more code, but i have only posted what i felt was the relevant stuff, if you need more to crack the puzzle message me!
You are not calling the good Hero instance in Door.DoorLocation.
Btw I really advice you to change class & methods name following Pep 8.
In Door.__init__, first line:
self.hero = Hero()
Here, you are instantiating a new Hero's instance. But, in game.MakeMap you are calling self.hero.HeroLocation().
This self.hero instance is not the same, because it was instantiated in game.__init__ and not in Door.__init__.
I didn't try, but check what behaviour gives this update:
class game:
def __init__(self, parent):
self.door = Door()
self.hero = self.door.hero
With this you now are calling the instance defined in Door.__init__, so when doing self.hero.HeroLocation() in game and (self.hero.heroC-self.doorC [...] in Door you are pointing the same instance.
Last thing, this solution may works, but is surely not what you really wants, I think a door should not store a hero, a hero should not store a door too, but here is more complex question about patterns.

How to correctly call on classes

I'm supposed to call on 2 different classes that then run the program so that the menu appears and the user chooses an option. I have most of the program running, but cannot check if anything is working because I keep getting the error that animal_type and name are not defined and am having trouble figuring out why. I've attached parts of each program.
Main file:
import Animal
import Zoo
def main():
#set user choice
animal = Animal.Animal()
Zoo.py:
import Animal
class Zoo:
#initialize attribute
def __init__(Animal):
Animal.__animals = []
def add_animal(Animal, animals):
Animal.__animals.append(animal)
def show_animals(Animal):
size = len(animals)
if size == 0:
print("There are no animals in your zoo!")
else:
return __animals
Animal.py:
import random
class Animal:
#initialize attributes
def __init__(self,animal_type, name, mood = None):
self.__animal_type = animal_type
self.__name = name
if mood:
self.__mood = mood
else:
self.check_mood()
# set methods
def set_animal_type(self, animal_type):
self.__animal_type = animal_type
def set_name(self, name):
self.__name = name
def set_mood(self, mood):
self.__mood = mood
In your main function, you aren't passing any of the required variables to Animal. (animal_type and animal)
You need to define them or just directly call with them.
animal = Animal.Animal('Goat', 'Billy')
or
atype = 'Goat'
aname = 'Billy'
animal = Animal.Animal(atype, aname)

Categories

Resources