simple taming calculator project - python

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

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.

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 :)

Changing attributes of an object in a python class

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):.

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.

Inheritance : transform a base class instance to a child class instance

I have an instance of a base class, and then I want to make it an instance of a child class of this base class. Maybe I'm taking the problem in a wrong way and there's something important I didn't understand in OOP. Code is only there to illustrate and a very different approach can be suggested. Any help appreciated.
class Car(object):
def __init__(self, color):
self.color = color
def drive(self):
print "Driving at 50 mph"
class FastCar(Car):
def __init__(self, color, max_speed=100):
Car.__init__(self, color)
self.max_speed = max_speed
def drive_fast(self):
print "Driving at %s mph" %self.max_speed
one_car = Car('blue')
# After the instanciation, I discovered that one_car is not just a classic car
# but also a fast one which can drive at 120 mph.
# So I want to make one_car a FastCar instance.
I see a very similar question, but none of the answers suits my problem :
I don't want to make FastCar a wrapper around Car which would know how to drive fast : I really want that FastCar extends Car ;
I don't really want to use the __new__ method in FastCar to make some tests on the arguments and decide if __new__ has to return a new instance of Car or the instance I gave to it (example: def __new__(cls, color, max_speed=100, baseclassinstance=None)).
class FastCar(Car):
def __init__(self, color, max_speed=100):
Car.__init__(self, color)
self.max_speed = max_speed
def drive_fast(self):
print "Driving at %s mph" %self.max_speed
#staticmethod
def fromOtherCar(car):
return FastCar(car.color)
actually_fast = FastCar.fromOtherCar(thought_was_classic)
This is the standard way.
Depending on the real class layout, you may be able to do something like:
classic = Car('blue')
classic.__class__ = FastCar
classic.__dict__.update(FastCar(classic.color).__dict__)
classic.drive_fast()
But I wouldn't recommend it -- it's a hack, it won't always work, and the other way is cleaner.
Edit: Was just about to add basically what #PaulMcGuire's comment says. Follow that advice, he's right.
You can borrow the C++ notion of a "copy constructor" to do something like this.
Allow Car's constructor to take a Car instance, and copy all of its properties. FastCar should then accept either Car instances or FastCar instances.
So then, to convert the car, you would just do one_car = FastCar(one_car). Note that this will not affect references to the original Car object, which will remain pointing to the same Car.
Why not just use one class?
class Car(object):
def __init__(self, color, max_speed = 50):
self.color = color
self.max_speed = max_speed
def drive(self):
print "Driving at %s mph"%self.max_speed
c=Car('blue')
c.max_speed = 100
It is not common in OOP to change type (class) of a living object after instantiation. I know barely two languages that would allow that as a dirty hack. The whole purpose of types (classes) is to know beforehand what operations an object can and can not perform. If you want something like this, you're probably mistaking the idea of OOP.

Categories