How to share properties and methods between classes? - python

Having been here: Python class inherits object, and here What is a metaclass in Python? I'm still unsure of how to get my code working. I'll be honest those explanations were a bit complicated for me. (BTW This is all in Python 3.4).
I'm trying to make Objects for my game in the form of creatures. Example: I want an Object called Dragon, but I want Dragon to inherit the class Entity, and the class Monster.
Here's what I've got:
class Entity:
id = 0
xLocation= 0
yLocation = 0
name = ''
description = ''
def __init__(self, id, xLocation, yLocation, name, description):
self.xLocation = xLocation
self.yLocation = yLocation
self.id = id
self.name = name
self.description = description
class Monster:
life = 0
pierce = 0
slash = 0
blunt = 0
pierceReduction = 0
slashReduction = 0
bluntReduction = 0
defenseBonus = 0
score = 0
def __init__(self, pierceReduction, bluntReduction, slashReduction, price, name, score):
self.pierceReduction = pierceReduction
self.bluntReduction = bluntReduction
self.slashReduction = slashReduction
self.price = price
self.name = name
self.score = score
class Structure:
pass
Monster = Entity
Dragon = Monster
I don't know how I can give Dragon all of the values of both an entity and a Monster? Obviously setting Dragon directly to monster doesn't work.
*****I've now tried this*****:
class Structure:
pass
class Dragon(Entity, Monster):
pass
Dragon(10, 10, 10, 1000, 'Dragon', 1000)
The Dragon is saying it doesn't have all the parameters of both classes though?

You can inherit from Entity class: class Monster(Entity): in declaration. You can find detailed information in documentation

Python can have classes inheriting from multiple parents. You can simply do:
class Dragon(Monster, Entity):
# class details
Just make sure that the resolution order between those classes is what you expect. Search for "python mro" for more details about it.

Related

How to inherit multiple instances of a class from a single class in python

Disclaimer: I am new to programming and have just started learning about Classes and Inheritance so perhaps it is my lack of understanding which is causing me issues?
I have created two classes in seperate files person.py and address.py.
The person class inherits from the address class however a person can have multiple addresses (postal, physical etc..) How can I inherit multiple instances of my address class to for each type.
My goal is to eventually have a library of common classes for future projects so any help or constructive comments around this concept are much appreciated.
I want to be able to create something like the below code where both the postal and the physical address utilise the Address class:
employee = Person()
employee.set_postal_address("342 Gravelpit Terrace","Bedrock")
employee.set_physical_address("742 Evergreen Tce", "Springfield")
Below is my code, I have not created any reference to Address() in the Person() class as I am not sure how to achieve this?
# address.py
class Address:
def __init__(self):
self.__line1 = None
self.__line2 = None
self.__town_city = None
self.__post_code = None
self.__state_region = None
self.__country = None
# Line 1
def get_line1(self):
return self.__line1
def set_line1(self, line1):
self.__line1 = line1
#etc....
# person.py
from address import *
class Person(Address):
def __init__(self):
self.__first_name = None
self.__last_name = None
self.__postal_address = []
self.__physical_adress = []
def set_postal_address(self):
# use the address class
# return a list of address values
self.__postal_address = []
def set_physical_address(self):
# use the address class
# return a list of address values
self.__physical_address = []
#etc...
Any help is greatly appreciated. If there is a better way of handling this please let me know.
You do not need to necessarily inherit Address when creating the class Person. One way to achieve your goal is to initialize an Address class object within the Person class.
# address.py
class Address:
def __init__(self, **kwargs):
self.__line1 = kwargs.get('__line1')
self.__line2 = kwargs.get('__line2')
self.__town_city = kwargs.get('__town_city')
self.__post_code = kwargs.get('__post_code')
self.__state_region = kwargs.get('__state_region')
self.__country = kwargs.get('__country')
# Line 1
def get_line1(self):
return self.__line1
def set_line1(self, line1):
self.__line1 = line1
#etc....
# person.py
from address import *
class Person:
def __init__(self, **kwargs):
self.__first_name = kwargs.get('__first_name')
self.__last_name = kwargs.get('__last_name')
self.__postal_address = None
self.__physical_address = None
def set_postal_address(self, **kwargs):
# use the address class
self.__postal_address = Address(**kwargs)
def set_physical_address(self, **kwargs):
# use the address class
self.__physical_address = Address(**kwargs)
#etc...
This allows you to keep inherited methods from getting cluttered in a given class object but still allows you to keep nice class structures.
Then you can do something like:
bob=Person(__first_name='Bob', __last_name='Smith')
bob.set_postal_address(__line1='42 Wallaby Way', __town_city='Sydney')
# Since we are using kwargs.get() any not specified Kwarg comes out as None.
# Now `__postal_address` is its own Address class and comes with Address methods
bob.__postal_address.set__line2('Apt 3B')
Now there are times when you would want to inherit common methods from higher level classes. You can read more on python classes here: (https://docs.python.org/3.6/tutorial/classes.html)

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

Change attribute in multiple objects

I'm learning OOP in Python and I get stucked with one thing.
I have an example class:
class Animal:
def __init__(self, name="", hunger=0):
self.name = name
self.hunger = hunger
def eat(self):
self.hunger += 1
And some objects:
dog = Animal("dog")
cat = Animal("cat")
giraffe = Animal("giraffe")
I would like to use method eat() to change value of hunger in every single one of them at one blow. I have already tried to do something like this:
Animal.eat()
But it doesn't work (there's TypeError, because of missing argument 'self').
Also:
Animal.hunger += 1
Doesn't work (returns AttributeError).
If anyone has any ideas, I would be very grateful!
You can maintain a class variable that collects the instances and adjust all of their hungers in eat:
class Animal:
instances = []
def __init__(self, name="", hunger=0):
self.name = name
self.hunger = hunger
Animal.instances.append(self)
def eat(self):
for i in Animal.instances:
i.hunger += 1
Semantically, you might want to make it a classmethod, though
#classmethod
def eat(cls):
for i in cls.instances:
i.hunger += 1
You can still call it on instances if you so wish.
#schwobaseggi has the most straightforward answer for what you want to do, but what you want to do seems like it's asking for trouble. You have one class that does two very different things. Animal is an animal that has a name and eats, and it also keeps track of every animal instance and makes all of them eat. Animal is trying to do what individual animals do and also control a group of animals.
It might be better to split this into two different kinds of objects: An animal, and some sort of AnimalGroup like Zoo or Farm or Herd. The AnimalGroup class should be responsible for keeping track of a bunch of instances and make them all do stuff.
class AnimalGroup(object):
def __init__(self, animal_list):
self.animals = animal_list[:]
def add_animal(self, animal):
self.animals.append(animal)
def all_eat(self):
for animal in self.animals:
animal.eat()
then
dog = Animal("dog")
cat = Animal("cat")
giraffe = Animal("giraffe")
group = AnimalGroup([dog, cat, giraffe])
group.all_eat()
group.add_animal(Animal("pig"))
group.all_eat()
This separates out the responsibilities of each class and makes things much easier to change later on. You can now have different group behaviors without ever needing to change the animal class. You can have new animal classes that inherit from Animal and you don't need to worry about side effects. for example: class Mammal(Animal) . When I call Mammal.eat, will it update all animals? It might. class variables can be a bit tricky like that. Should it update all animals? No idea. With an AnimalGroup object, you don't need to worry.
You actually have to call it on the object itself like this:
cat.eat()
dog.eat()
giraffe.eat()
otherwise it doesn't know which object to actually change. You could store all your Objects in an array and loop over that array to call the function on all of them one after another:
dog = Animal("dog")
cat = Animal("cat")
giraffe = Animal("giraffe")
animals=[dog, cat, giraffe]
for animalType in animals:
animalType.eat()
now you can do them all at once or one at a time if you want. You will however need to addnew animals to the array after you create them to keep the list up to date:
fish=new Animal("fish")
animals.append(fish)
class Animal(object):
hunger = 0
def __init__(self, name=""):
self.name = name
def eat(self):
Animal.hunger = Animal.hunger + 1
dog = Animal("dog")
cat = Animal("cat")
giraffe = Animal("giraffe")
dog.eat()
print("Dog's hunger variable is", dog.hunger)
1
dog.eat()
print("Dog's hunger variable is :",dog.hunger)
2
print("Cat's hunger variable is :",cat.hunger)
2
print("Giraffe's hunger variable is :", giraffe.hunger)
2
When eat() is called on a single instance, the hunger variable is updated for all instances!
If you're wanting to do something on the class you have to declare it as a class variable:
class Animal:
hunger = 0
def __init__(self, name=""):
self.name = name
#classmethod
def eat(klass):
klass.hunger += 1
This way anytime you call Animal.eat() you'll be referencing the class method that modifies your class variable. You can still access the variable from within an Animal class with self.hunger but I would advise against that as it can get confusing coming back and trying to determine what's a class variable and what's a member variable.
To the best of my knowledge (and I really like OOP in python), the only way to do this is to create a new class with that specific attribute a.k.a.
class Animals:
def __init__(self, animals):
self.animals = animals
def all_eat(self):
for animal in animals:
animal.eat()
Then what you would have to do is:
dog = Animal("dog")
cat = Animal("cat")
giraffe = Animal("giraffe")
animals = Animals((dog, cat, giraffe))
animals.all_eat()
The reason for this is that python classes themselves do not have callable attributes so you have to call each instance of the class separately.

object and attributes in python h.w

I have h.w to built a simple class.
I have problems with two def: DisOwnerCars and DisAllOwnerCars.
this is my code (after corrections):
class Car:
def __init__(self, manufacture, production_year):
self.manufacture = manufacture
self.production_year = production_year
def __str__(self):
return '{} {}'.format(self.manufacture, self.production_year)
class CarOwner:
car_owners = []
all_cars = []
def __init__(self, name):
self.name = name
self.cars = []
CarOwner.car_owners.append(self)
def add_car (self, car):
self.cars.append(car)
CarOwner.all_cars.append(car)
def DisAllCars():
for owners in CarOwner.car_owners:
for car in owners.cars:
print(car)
def DisAllCarsSorted():
print(sorted(CarOwner.all_cars, key=lambda x: x.manufacture))
def DisOwnerCars(car_owner):
for car in car_owner.cars:
print(car)
def DisAllOwnerCars():
for owners in CarOwner.car_owners:
print('Cars owned by {}:'.format(owners.name))
for car in owners.cars:
print(car)
jane = CarOwner("Jane")
jane.add_car(Car("Mitsubishi", 2017))
bob = CarOwner("Bob")
bob.add_car(Car("Mazda", 2013))
bob.add_car(Car("BMW", 2012))
DisOwnerCars(jane)
DisAllOwnerCars()
DisAllCarsSorted()
DisAllCars()
I still gets some errors in the DisAllCarsSorted().
the error:
File "", line 3, in DisAllCarsSorted
print(sorted(CarOwner.all_cars))
TypeError: '<' not supported between instances of 'Car' and 'Car'
You seem to get the purpose of classes differently than what it comes to provide.
Classes are prototypes - like reciepe or a blueprint - for creating instance. Therefore, the class CarOwner is a reciepe for what properties and actions can a car owner have. You should not use classes as containers to data, like collecting a list of all owners. That is the use case for lists, and possibly metaclasses.
If you want the list of all car owners, you should create several instances of a CarOwner and keep them in a list, not save all of the data in a set inside the different instances.
I've modified your code a little bit, to provide an example for what does a simple OOP looks like and how to encapsulate the classes; note the abstraction of the class Car, the use of __str__, and the initializer, that do nothing but initialize; I hope it will serve as a good example.
class Car:
def __init__(self, manufacture, production_year):
self.manufacture = manufacture
self.production_year = production_year
def __str__(self):
return '{} {}'.format(self.manufacture, self.production_year)
class CarOwner:
def __init__(self, name):
self.name = name
self.cars = []
def add_car (self, car):
self.cars.append(car)
def print_cars (car_owner):
for car in car_owner.cars:
print(car)
def print_owners (car_owners):
for car_owner in car_owners:
print('Cars owned by {}:'.format(car_owner.name))
for car in car_owner.cars:
print(car)
jane = CarOwner("Jane")
jane.add_car(Car("Mitsubishi", 2017))
bob = CarOwner("Bob")
bob.add_car(Car("Mazda", 2013))
bob.add_car(Car("BMW", 2012))
owners = [jane, bob]
print_cars(jane)
print('===')
print_owners(owners)

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

Categories