Changing attributes of an object in a python class - python

So, my question is, if I were to make a Race based on this class, say, an elf. I make the elf like this:
elf = Race('Elf', 100, 50, 60, 90)
If the elf got into combat, how do I make his health go down in that one instance, or any of his other stats go up based on a bonus of some sort, like if he had some kind of equipment giving him +(stat)?
Kind of a specific question, I know.
Here is the class Race I have set up so far. . .
class Race(object):
race = None
health = None
strength = None
speed = None
endurance = None
def __init__(self, race, health, strength, speed, endurance):
self.race = race
self.health = health
self.strength = strength
self.speed = speed
self.endurance = endurance
def set_race(self, race):
self.race = race
def set_health(self, health):
self.health = health
def set_strength(self, strength):
self.strength = strength
def set_speed(self, speed):
self.speed = speed
def set_endurance(self, endurance):
self.endurance = endurance
Criticism is welcome, so long as its constructive!

Well, for one, the code wouldn't compile due to IndentationError but that's besides the point I suppose (so, please indent with 4 SPACES, not tab as per PEP-8 for each function body and class body).
With that said, you don't need any getters or setters because it's not pythonic. If you want to change the attribute of an object, just change the attribute. If you need to control how an attribute is set, then use properties.
Also, in your code below, you are actually not setting instance variables, you are setting member variables (please keep in mind I fixed the indentation):
class Race(object):
race = None
health = None
strength = None
speed = None
endurance = None
If you were to remove the above attributes but kept all the self.* in the __init__() function, you would then have instance attributes. The term self.* is stating "this is an attribute of myself, of my exact instance of me".
Also, please consider renaming race. You want a class to encapsulate all the attributes of an object. Think about it: a character's race doesn't have a health. A character can have a health, but the character also has a race. Both of these things are attributes of the character. I would suggest naming this class to class Character(object):.

Related

How to initialize subclasses of an abstract class?

I'm trying to create a dark souls style text-based game. I've created an abstract class which outlines the characteristics of a dark souls class/character type. There are a couple of ways I have initialized it and I'm not sure if one is better than the other.
One important note is that in all of the subclasses, the only parameter I want is the name parameter, while all other variables will be set equal to values specific to that class/character type.
Here is one of the abstract classes I created and its subsequent subclass:
class Character(ABC):
def __init__(self, name, description, health, endurance, strength) -> None:
self.name = name
self.description = description
self.health = health
self.endurance = endurance
self.strength = strength
class Giant(Character):
def __init__(self, name) -> None:
super().__init__(name, description="giant", health=500, endurance=20, strength=100)
Here is the 2nd version:
class Character(ABC):
def __init__(self, name) -> None:
self.name = name
self.description = ''
self.health = 0
self.endurance = 0
self.strength = 0
class Giant(Character):
def __init__(self, name) -> None:
super().__init__(name)
self.description = "giant"
self.health = 500
self.endurance = 20
self.strength = 100
Is one way better than the other? Is there a totally different way which would be better? I'm pretty new to inheritance and abstract classes and I'd appreciate any help you'd be able to provide. Thanks!
I would indeed use neither approach, at least for what you've described here so far in this example.
Is the only way that different subclasses of Character are different that their stats are different? Or would they actually have different behavior?
Because if it's really only about different values, I'd instead just make Character a concrete rather than abstract class and then provide certain methods:
class Character:
def __init__(self, name, description, and so on):
set the values here
#classmethod
def create_giant(cls, name):
return cls(name=name, description="giant", health=500, and so on)
And then you'd make a giant like so:
my_giant = Character.create_giant(name="Olbrzym")
For your versions, they have slightly different semantics. In your version 1, someone calling super().__init__ will be forced to provide concrete values, whereas in version 2, they can just rely on the default values. Given that a character with health=0 probably doesn't make sense, I'd favor version 1.
You see that my version doesn't use inheritance. When would I use inheritance? When I can't easily differentiate the various character types (Giant, Dwarf, Elf?) through their health and endurance and strength values alone but actually need different behavior.
Like, if you imagine keeping with the simple approach and you end up with code that uses a lot of constructs like
if self.description == 'Giant':
do_giant_stuff()
elif self.description == 'Dwarf':
do_dwarf_stuff()
elif AND SO ON
that's a good sign you should be using inheritance instead.
EDIT:
So, to have the classes different behavior, version 1 or 2? Either would work, honestly. But there's a third way. Might be overkill but might come in handy: Hook methods.
Here's how that goes: Write things so that subclasses don't have to call super().__init__. Instead, write the init in the abstract class but have it call abstract methods to fill in the default values.
class Character(ABC):
def __init__(self, name):
self.name = name
self.description = self._get_class_description()
self.health = self._get_class_health()
...
#abstractmethod
def _get_class_description():
pass
... same for the other attributes
class Giant(Character):
def _get_class_description(self):
return "giant"
def _get_class_health(self):
return 500
...
It's called the hook method because the abstract base class describes the methods that a subclass should specify (the hooks) in order to fill in the gaps in behavior.

simple taming calculator project

class tame_dilo:
torpor = 250
def __init__(self, name, effect):
self.name = name
self.effect = effect
def attack(self):
self.torpor = self.torpor - self.effect
dilo = tame_dilo('dilo', 25)
dilo.attack()
print(dilo.torpor)
class tame_sable(tame_dilo):
torpor = 500
sable = tame_sable('sable', 25)
sable.attack()
print(sable.torpor)
I just started learning some oop on python and I decide to do this little project to practice a little.
What I want to know is, if Im using the proper way to relate the name of the creature with its torpor by using inheritance and some polymorphism to define a diferent torpor according to the creatur class.
And also i want to know what would be the proper method so the user can change the effect of the attack method like if you were using better equitment to knock the creature.
A dilo and a sable are a type of tame. They are instances, not classes.
Therefore, you need one class capable of holding different attributes.
Also, assuming torpor is health, or energy, I'm not sure why the attack function is affecting itself. Shouldn't an instance be attacking something else?
class Tame:
def __init__(self, name, effect, torpor):
self.name = name
self.effect = effect
self.torpor = torpor
def attack(self, other):
other.torpor -= self.effect
Now you create named instances
dilo = Tame('dilo', 25, 250)
sable = Tame('sable', 25, 500)
dilo.attack(sable)
print(sable.torpor)
To change the effect of a tame, just update it
dilo.effect += 10

How do I use Class Attributes, Arguments and "self." effectively in Python?

I am creating a text-based adventure game (as mentioned in this post), and I am stuck on how to use Classes and their attributes, arguments related to classes, and the pre-defined keyword self.. I have looked around for how to use them, but to no avail. I will give an example of each, with a question attached to them:
class Player:
def __init__(self, name):
self.name = name
self.maxhealth = 100
self.health = self.maxhealth
self.attack = 15
self.money = 0
def display(self, name):
print("Name:",self.name,"\nHealth:",self.health,"/",self.maxhealth,"\nAttack Damage:",self.attack,"\nMoney:",self.money)
Someone from the answers suggested changing self.name = name to self.name = None, but they didn't explain why or how it would help. If anyone could tell me how to define self.name in the correct or most efficient way, please do. I would also like to know how to call it in later functions.
I am confused on how class attributes work (related to previous question), and how you know if they are enabled, and how to call them.
I am also unsure of the arguments that would go into class Player(#here):, and how they could be used. I'm not sure if there are any uses to it, but I have seen people do (object) and (Edum #or something like that) and I don't know what they add, how to call them etc.
import time
import random
import sys
import pickle
class Zombie:
def __init__(self, name):
self.name = name
self.maxhealth = 50
self.health = self.maxhealth
self.attack = 5
self.golddrop = random.randint(5,10)
class Skeleton:
def __init__(self, name):
self.name = name
self.maxhealth = 75
self.health = self.maxhealth
self.attack = random.randint(5,10)
self.golddrop = random.randint(5,10)
class Friend:
def __init__(self, name):
self.name = name
self.maxhealth = 100
self.health = self.maxhealth
self.attack = random.randint(7,10)
class DungeonBoss:
def __init__(self, name):
self.name = name
self.maxhealth = 150
self.health = self.maxhealth
self.attack = random.randint(15, 25)
Those are the classes I want in my game. I am wondering whether I can make a class just called "enemies" and then maybe make sub-sets/classes that can then be called from that class (once again, any help with how to call attributes from classes will be much appreciated), so it makes the code a lot cleaner.
Please leave a comment if you are unsure about anything I just said, or if you need some more clarification with the questions. Thank you in advance.
Ok so quick lesson in Python Classes. First, lets cover self. Self refers to the Object you create for that class. So lets say:
zombie1 = Zombie("bob")
self in this instance refers to the object "zombie1"
Now, to use attributes of methods. Think of a class as something that encapsulates some generic data. You have that part down it seems already. Now the attributes are those generic data types that the class contains. So for instance, your zombies name, attack, etc. To access them, we simply do as such:
zombie1.name
'bob'
zombie1.attack
5
So on and so forth. This will give you the data that is attached to those attributes.
Now to make sub classes, you could do something like this.
class Enemies(object):
def __init__(self):
self.maxhealth = 100
class Zombies(Enemies):
def __init__(self, name):
super().__init__()
self.name = name
self.health = self.maxhealth
self.attack = 5
self.golddrop = random.randint(5, 10)
You would assign an object to it like so:
zombie2 = Zombie("Mike")
The output such as this, will give you the data from the Zombie class and the Enemies Class:
zombie2.maxhealth
100
zombie.name
'Mike'
zombie2.attack
5
So on and so forth. If you look carefully, you will see that zombie2 has the attribute maxhealth, even though that is initialized and defined under Enemies. Hope this helps some!
Ill leave the enemies class up to you as far as how you wish to create generic enemy data to be used by individual monsters. I just wanted to use maxhealth as an example to show it can pull from the parent class in a sub class.
Response to comment question:
To get a players name, you can go one of two ways, create a method, or ask for it when programming it out. I will show you both ways.
class Player(object):
def __init__(self):
self.name = None
self.gold = None
def getName(self):
self.name = input("Please enter your name: ")
print(self.name)
player1 = Player()
player1.getName()
When you call player1.getName() it will ask for their name and store it to the name variable. You can then call there name via:
player1.name
'Peter'
You could also do:
class Player(object):
def __init__(self, name):
self.name = name
self.gold = None
This way you can call the players name on the object creation such as:
player1 = Player(input("Please enter your name here: "))
player1.name
'Peter'
This way allows you to specifically code a name, or ask the user for one.
Last edit for the gold bit here:
class Player(object):
def __init__(self, name):
self.name = name
self.gold = None
def giveGold(self):
print("Since you are new, 100 gold dubloons have been given to you!")
self.gold = 100
So this allows you to call Player() how ever you wish. You can hardcode a name, or ask for user input. Then run the method giveGold() on the Player() object to grant them 100 gold Dubloons.
player1 = Player("Peter")
player1.giveGold()
Since you are new, 100 gold dubloons have been given to you!
player1.gold
100
Hope that makes sense. I think I have helped get you started quite well. I will leave the rest of the game making to you :)

When does a class warrant a sub-class?

For example, I am trying to build an enemy class for a simple game. Each enemy that spawns has a type which affects it stats which are fields in the enemy class.
class Enemy:
#Base Stats for all enemies
name = "foo"
current_health = 4
max_health = 4
attack = 0
defense = 0
armor = 0
initiative = 0
initMod = 0
alive = True
Should each type be a subclass of enemy like so..
class goblin(Enemy):
name = goblin;
current_health = 8
max_health = 8
attack = 3
defense = 2
armor = 0
def attack(){
//goblin-specific attack
}
But this method means that I would have to build a class for each individual type (which would be 80+ classes), or is there a better way to do it? This enemy is going to be randomized, so I was thinking the types could also be put into a dictionary which uses the names of the types as keywords. Although I'm not entirely sure how I could implement that.
If you want to go the dictionary route you could do something like this with a tuple being returned by the key:
enemy_types = {"goblin": ("goblin", 8, 8, 3, 2, 0, goblin_attack)}
def goblin_attack(enemy):
do_stuff()
Though you may want to use a named_tuple
https://stackoverflow.com/a/13700868/2489837
to make sure you don't mix up the fields in the tuple
If the set of attributes and possible actions for each enemy type is mostly common across types, I don't see a reason to do anything more complicated than defining an 80 item enumeration for the type, and using a single class called Enemy.
If you do need a variety of actions and such, perhaps you could group your 80 types into various collections that would then have common attributes (e.g. FlyingEnemy, UndeadEnemy, etc). Those would then become your sub-classes.
The goblin should probably be an instance of Enemy. However, in your example you added a method called attack. I would say the best way to do this is like this:
class Enemy:
#Base Stats for all enemies
def __init__ (self, **kwargs):
self.name = "foo"
self.current_health = 4
self.max_health = 4
self.attack = 0
self.defense = 0
self.armor = 0
self.initiative = 0
self.initMod = 0
self.alive = True
# Use this to specify any other parameters you may need
for key in kwargs:
exec ("self.%s = %s" % (key, kwargs[key])
Now you can create an instance of Enemy called goblin.
To answer your initial question of when to use a subclass, I would say that it is great to use sub-classes but may over complicate things in your case. Since you only have one method in your subclass you can easily just make a module and then have an argument in the __init__ that specifies the attack function. This will simplify your code more.
Note: If you are going to use subclasses make sure that in the __init__ of your subclass you call the __init__ of your baseclass with super().

Easy way to set multiple class attributes?

For example, if have a 'creature' class:
class Creature:
def __init__(self,name,hp,damage):
self.name = name
self.hp = hp
self.damage = damage
Now if I want to create an orc, I would do orc = Creature(0,0,'orc',20,4). However, with multiple types of creatures (troll, ogre, etc), this would be impractical: I have to remember the stats for each type of creature, and every time I want to create a creature I have to enter all those different stats.
Is there a way to do this in an easier way? I would for example do orc = Creature(orc), and it would automatically assign its name, hp and damage, which I defined in a different file/part of the code, in a dictionary for example.
Is this possible?
EDIT: I know about inheritance and all the stuff that comes with it, but I read somewhere that making child classes which just set the attributes of the parent to a set value is "not good", although I can't recall the exact reason.
You can always use a prototype:
proto = {
"orc" : (200, 300, 0)
"troll" : (500, 300, 0)
"wolf" : (100, 100, 0)
...
}
class Creature:
def __init__(self, name):
self.name = name
self.hp = proto[name][0]
...
The above of course is not complete or safe. But the point is that if you need to create many object of the same "type" just keep the default values aside someplace else.
You definitely want to use inheritance.
You should really read some tutorial on OOP before writing a game in an object-oriented language.

Categories