Classes, objects and lists in python - python

so I'm trying to create a program but I still have difficulties with classes. The program (which isn't finished of course) will print a random number of Villages. Each village(second Class) will have a random number of Clans(the first Class). Anyway my problem is the Village class. How do I make sure to add the areas and family size into the Village class? How do I insert the counter from the Clan class to the Village class? As you can see when I've randomized a number of Clans the areas and family sizes should add up into the Village class. What should I do? What is wrong with my Village class?
class Clan:
counter = 0
def __init__(self):
r = random.randrange(20, 101)
self.area = r
s = random.randrange(1, 6)
self.familySize = s
Clan.counter += 1
self.counter = Clan.counter
def getArea(self):
return self.area
def getFamilySize(self):
return self.familySize
class Village:
counter = 0
def __init__(self):
self.clan_list = []
for i in range(random.randrange(3, 7)):
self.clan_list += [Clan()]
def getclan_list(self):
return self.clan_list
def getArea(self):
return self.area
def getPopulation(self):
pass

So, you want the village class to calculate how many families are in the village?
families = 0
for clan in self.clan_list
families += clan.getFamilySize()

Since the values of area and population are dependent on the clans in clan_list you should compute these values each time they are needed. The alternative is much more complicated -- having to control how clans are added and removed from the village and how the area and family size of a clan can be changed and having those changes reflected in the village.
Below is an example of how you might compute both village area and population. The first using a getter method, and the second using a more python-esque property.
import random
class Clan:
counter = 0
def __init__(self):
r = random.randrange(20, 101)
self.area = r
s = random.randrange(1, 6)
self.familySize = s
Clan.counter += 1
self.counter = Clan.counter
# removed getters
# Unless your getter or setter is doing something special,
# just access the attribute directly.
class Village:
def __init__(self):
self.clan_list = []
for i in range(random.randrange(3, 7)):
self.clan_list.append(Clan())
# for a village, area is a computed value, so use a getter
def getArea(self):
total_area = 0
for clan in self.clan_list:
total_area += clan.area
return total_area
# the prefered alternative to getters (and setters) are properties
# note that the function is named as if it was an attribute rather than function
#property
def population(self):
# use sum and generator rather than a for loop
return sum(clan.familySize for clan in self.clan_list)
# How to use a village instance
v = Village()
# get area
print("area:", v.getArea())
# get population
print("population:", v.population)
# note how population is accessed as if it was an attribute rather than called
# like a function

Related

Python getting parent class function to child classes instance

I want to have a function in my parent class that when called by child class it will create another instance of that child class. I have an ugly solution but there must be better one. Here is my solution (this is in the function within the parent class):
if type(self) == MountainAsh:
trees.append (MountainAsh(seedlingLocation,0)) #Adds new seedling to trees list
elif type(self) == TeaTree:
trees.append (TeaTree(seedlingLocation,0)) #Adds new seedling to trees list
A little more code for context: (feel free to give other pointers, I am new to this, particularly OOP approaches)
class Tree(): #Tree parent class
def __init__(self, location, age):
self.location = location #tuple (x,y)
self.age = age
self.fertility = .5 #fertility betweeen 0 and 1
self.seedRange = 3 #Range from tree that seed can land
self.shade = 1 #Shade preventing new seedlings
self.lifeSpan =10 #tree dies at this age (no random currently)
def reproduce(self):
if random.uniform(0,1)<self.fertility: #Does it produce a seed?
distance = random.uniform(0,self.seedRange) #random range
angle = random.uniform(0,2*math.pi) #random sirection
#calculation of seed location
seedlingLocation = (self.location[0]+distance*math.cos(angle),self.location[0]+distance*math.sin(angle))
if seedlingLocation[0]>forestSize[0] or seedlingLocation[1]>forestSize[1] or min(seedlingLocation)<0:
return #checking if seed falls outside of forest therefore it doesn't grow
for i in trees:
if math.dist(i.location,seedlingLocation)<i.shade: #Checking if seed is outside of shade of existing tress
return
if type(self) == MountainAsh:
trees.append (MountainAsh(seedlingLocation,0)) #Adds new seedling to trees list
elif type(self) == TeaTree:
trees.append (TeaTree(seedlingLocation,0)) #Adds new seedling to trees list
#set up long lived tree
class MountainAsh(Tree):
def __init__(self, location, age):
super().__init__(location, age)
self.fertility = 0.3 #lower fertility
self.seedRange = 3 #larger
self.shade = 1
self.lifeSpan =15 #longer lived
#set up short lived tree
class TeaTree(Tree):
def __init__(self, location, age):
super().__init__(location, age)
self.fertility = 0.8 #high fertility
self.seedRange = 2 #smaller
self.shade = .5
self.lifeSpan =5 #Short lived

How can I automate the creation of instances of a class?

So in python 3 I am having trouble creating multiple instances of a class automatically. I am trying to make monopoly and here is the code sample that is giving me problems.
def numplayer():
numplayer = int(input('How many players would you like? (up to four)'))
while numplayer > 4 or numplayer < 1:
numplayer = int(input('How many players would you like? (up to
four)'))
for i in range(numplayer):
PlayerMoney.append(1500)
What I want to do is also add something that will create the number of players that numplayers equals to in the for i in range(numplayer) function. I have the player as a class but I don't want to manually create every single class for every player. If there is a solution to this, please do tell. Thanks!
EDIT: So I think this might be bad wording in the title but I'm trying to create multiple instances of a single class (the player).
Here is the code for the player class:
class Player:
def __init__(self, name, money, position):
self.name = name
self.money = money
self.position = position
def DiceRoll(self):
x = random.randint(1, 6)
y = random.randint(1, 6)
sum = x + y
return [sum, x, y]
def getName(self):
return sef.name
def getMoney(self):
return self.money
def getPosition(self):
return self.position
# Create Class
class Player:
def greating(self):
print 'Hello!'
# List to store instanses
l = []
for i in range(4):
l.append(Player())
# Call Instance #1 methods
l[0].greating()
Here we have a player class and 4 instances from this class stored in l list.
I would advise you structure your code as below. It's usually a good idea for your function to return something.
def setup():
n = int(input('How many players would you like? (up to 4)'))
names = [input('Give name #{0}'.format(i)) for i in range(1, n+1)]
return [Player(name, 1500, 0) for name in names]
players = setup()

How to iterate for loop with values in different methods?

I made this program. What I want to do now, is to make a new method in the class Star_cluster that will count and return all planets in the star cluster that are in danger. In this case, a planet in danger is a planet with a star that has a short lifespan. A star has a short lifespan when it's total mass is more than 9.5 solar masses.
class Planet:
def __init__ (self, a_str1, a_str2, a_float):
self.name = a_str1
self.type = a_str2
self.orbital_period = a_float
class Star:
def __init__ (self, a_float, a_planet_list):
self.total_mass = a_float # in solar masses
self.orbiting_planets = a_planet_list # list of Planet-objects
class Star_cluster:
def __init__ (self):
self.star_list = [] # list of Star-objects
def add(self, Star):
self.star_list.append(Star)
My problem is that I don't get how I should combine the values in the different methods to make one for loop.
For example, I tried to make something like this:
class Star_cluster:
def planets_in_danger (self):
for i in self.orbiting_planets:
if i.total_mass > 9.5:
k = self.orbiting_planets.count(i)
return k
But I can't get this working. I want to understand how I should approach this problem, because I know what to do, but not entirely sure about how to do it the best way.
Especially whether to use self and the fact that there are multiple methods involved and just one for loop, confuses me. Could someone point me in the right direction?
Loop over all the stars in the given cluster. If a star's total mass exceeds 9.5, count the amount of planets that belong to that star to the amount of endangered planets.
def planets_in_danger(self):
in_danger = 0
for star in self.star_list:
if star.total_mass > 9.5:
in_danger += len(star.orbiting_planets)
return in_danger
Or shorter:
def planets_in_danger(self):
return sum(len(star.orbiting_planets) for star in self.star_list if star.total_mass > 9.5)
edit: returning the count of endangered planets and the planets:
def planets_in_danger(self):
in_danger = []
for star in self.star_list:
if star.total_mass > 9.5:
in_danger.extend(star.orbiting_planets)
return (len(in_danger), in_danger)
Call the function planets_in_danger in Star_Cluster
class Planet:
def __init__ (self, a_str1, a_str2, a_float):
self.name = a_str1
self.type = a_str2
self.orbital_period = a_float
class Star:
def __init__ (self, a_float, a_planet_list):
self.total_mass = a_float # in solar masses
self.orbiting_planets = a_planet_list # list of Planet-objects
class Star_cluster:
def __init__ (self):
self.star_list = [] # list of Star-objects
def add(self, Star):
self.star_list.append(Star)
def planets_in_danger(self, stars_list):
result = []
solar_mass = 1.989 * (10**30)
for i in stars_list:
if i.total_mass > 9.5 * solar_mass:
result.append(i)
return result

Python - Classes- Definition

I programmed a class aswell as a definition with the backthought the when you,
set everytime a value using class variable you can always recall the total amount by using the definition
class Hand():
def __init__(self, Hand=0):
self.Hand = Hand
def getHand(self, neue_Hand):
self.Hand = neue_Hand
def set_hand(self):
return self.Hand
c = Hand()
def Aufruf():
Total = 0
Total += c.getHand(0)
return Total
c.getHand(12)
Aufruf()
It changes the value each time, but doesn't accumulate it as it supposed to be.
You've mixed up the functionality in your getters and setters. The getter should return the variable and the setter should set the value.
class Hand():
def __init__(self,Hand=0):
self.Hand = Hand
def getHand(self):
return self.Hand
def set_hand(self, neue_Hand):
self.Hand = neue_Hand
def increment_hand(self, neue_Hand_incremenet):
self.Hand += neue_Hand_incremenet
c = Hand(10)
c.getHand()
>> 10
c.set_hand(20)
c.getHand()
>> 20
def Aufruf():
Total = 0
Total += c.getHand()
return Total
Aufruf()
>> 20
c.increment_hand(10)
Aufruf()
>> 30
Also as a side note:
If you look closely, you will realise your method Aufruf is actually an exact duplicate (logically) of the getHand() method. When you instantiate the variable total = 0 inside the method block of code, this value will ALWAYS be set to 0 when the method is called, meaning the value from c.getHand() will ALWAYS just be the value that's returned
Use method addition for changing value, and don't use capital letters or camelCase inside your class.
class Hand:
def __init__(self, 0):
self.hand = hand
def get_hand_bigger(self, addition):
self.hand += addition
Your class method getHand does not return any value, yet it is being called as such. Try this:
def getHand(self, neue_Hand):
self.Hand = neue_Hand
return self.Hand

Python - Compare two lists and find matching values and their positions in the list

I am trying to make a turn based RPG and I am currently working on the turn system.
To keep it simple for now I am trying to order each character/enemy by their speed and then highest speed goes first. Below is the start of my code for the turn system.
An example of what I want to do is read Order[0] (which will be the fasted character/enemy) and find out which character/enemy that relates to. Speed[0] to Speed[3] gives the speed of my 4 characters and enemySpeed[0] to enemySpeed[4] gives the speed of the 5 enemies.
def Turn():
Turn = [int(Speed[0]), int(Speed[1]), int(Speed[2]), int(Speed[3]), int(enemySpeed[0]), int(enemySpeed[1]),int(enemySpeed[2]), int(enemySpeed[3]), int(enemySpeed[4])]
Order = sorted(Turn, key = int, reverse = True)
Edit: Here is some information regarding player stats.
In main():
name = ['Ben','Ellis','Curt','Jen']
HP = [100,100,100,100]
exp = [0,0,0,0]
lvl = [1,1,1,1]
player1 = [name[0],HP[0],exp[0],lvl[0]]
player2 = [name[1],HP[1],exp[1],lvl[1]]
player3 = [name[2],HP[2],exp[2],lvl[2]]
player4 = [name[3],HP[3],exp[3],lvl[3]]
PLAYERS = [player1, player2, player3, player4]
REGION = 'start'
POS = '[x, y]'
ITEMS = None
SIGEVENTS = False
gameData = [PLAYERS, REGION, POS, ITEMS, SIGEVENTS]
Out of main():
def playerStats():
global Attack, Defense, Speed, MaxHP
Attack = [0,0,0,0]
Defense = [0,0,0,0]
Speed = [0,0,0,0]
MaxHP = [0,0,0,0]
for i in range(0,4):
Attack[i] = lvl[i] * 1
Defense[i] = lvl[i] * 2
Speed[i] = lvl[i] * 3
MaxHP[i] = lvl[i] * 4
return Attack, Defense, Speed, MaxHP
Instead of looking at just the speeds, look at your players and enemies, and devise a key to determine what speed goes with what player.
You didn't share any details on how you define players and speeds; I'll use a number here for now:
players_and_enemies = range(8)
fastest = max(players_and_enemies, key=lambda p: int(Speed[i] if i < 4 else enemySpeed[i - 4]))
It may well be that you can simplify this based on your data structures; if you have objects per player and enemy that have a speed attribute, for example, you can access that attribute in the key function instead.
max() returns the highest value by key; no need to sort all speeds if all you need is the fastest driver.
Martijn Pieters already pointed out the most important issues. Try to use objects. Try to remodel the objects of your game and don't manage their attributes seperately.
I will elaborate a bit on this and maybe I can give you so some ideas.
A Character, be it a NPC or a PC, is one object of a class that could look something like this:
class Character:
def __init__ (self, name, xp = 0, level = 1, hp = 4, speed = 3, atk = 1, def_ = 2):
self.name = name
self.xp = xp
self.level = level
self.hp = self.curHp = hp
self.speed = speed
self.atk = atk
self.def_ = def_
self.inventory = []
Now using this class you can instantiate your PCs and NPCs:
npcs = [Character (name) for name in ['Baldur', 'Lord Never', 'Ashyra'] ]
Adding methods to your class lets you interact with it, for example:
class Character:
[....]
def takeDamage (self, dmg):
self.curHp -= min (0, dmg - self.def_)
def gainXP (self, xp):
self.xp += xp
#check for level up
def takeItem (self, item):
self.inventory.append (item)
def useItem (self, item):
item.activate ()
self.inventory.remove (item)
Or in order to get all your characters ordered by their speed, you can use:
charactersBySpeed = sorted (characters, key = -character.speed)
And so further and so on. The idea is really to use object oriented programming for modelling the reality (even the made-up reality of your game) in your code.

Categories

Resources