One thing that I am struggling with while trying to learn concepts of OOP is creation of class instances. Most of the tutorials online will explain basic principles like Init, Self, Inheritance etc.. but when it comes to creating instances of the class itself it is usually reduced to something like that:
emp1 = Employee("John")
emp2 = Employee("Leviticus")
In reality most of us beginners will want to create instance of a class dynamically (On press of button etc..) not directly in code and also will be interessted in keeping track of our instances. What I was able to come up is this:
from tkinter import *
import random
class Point:
_registry = []
def __init__(self, x_pos, y_pos):
self._registry.append(self)
self.x_pos = x_pos
self.y_pos = y_pos
print(self.x_pos, self.y_pos)
def create_point():
Point(random.randint(1,20),random.randint(1,20))
window = Tk()
button = Button(window, text = "Add point", command=create_point)
button.pack()
window.mainloop()
Can someone advise if this is a proper way to do this? Shouldnt the fnction create_point be within Point class? What is the proper way to keep track of instances and later delete them? Shall I use some sort of ID attribute to keep track and "itemize" my instances? Is there any good source with tutorial that deals with that?
Thank you
Jacob
after completing tutorial at: https://pythonschool.net/category/oop.html I managed to get what I wanted by doing:
class Point:
def __init__(self,ID, xcor, ycor):
self._ID = ID
self._xcor = xcor
self._ycor = ycor
def report(self):
return {"ID:":self._ID,"xcor":self._xcor,"ycor":self._ycor}
def get_ID(self):
return self._ID
class Points:
def __init__(self):
self._points = []
def add_point(self, point):
self._points.append(point)
def return_index_from_ID(self, ID):
for i, o in enumerate(self._points):
if o.get_ID() == ID:
break
return i
def delete_point(self, index):
del self._points[index]
def print_contents(self):
for x in self._points:
print(x.report())
def return_empty_ID(self):
list = []
for x in self._points:
list.append(x.get_ID())
if not list:
return 1
else:
for i in range(1, max(list)+2):
if i not in list: break
return i
def add_point( xcor, ycor, points):
points.add_point(Point(points.return_empty_ID(), xcor, ycor))
def delete_point(ID, points):
points.delete_point(ID)
Simple main function for testing to show what I was after:
from point_class import *
myPoints = Points()
noexit = True
while noexit:
print("**********************************************")
print("0 - Exit")
print("1 - Add Point")
print("2 - Print Points")
print("3 - Delete Points")
print("**********************************************")
choice = int(input("Option selected: "))
if choice == 0:
noexit = False
elif choice == 1:
add_point(3,5,myPoints)
elif choice == 2:
myPoints.print_contents()
elif choice == 3:
ID = int(input("Please insert ID of point: "))
delete_point(myPoints.return_index_from_ID(ID),myPoints)
Related
I need help to be more clear how to make my code correctly. Now it's work but I still think the code logic not correct :)
I have one class with several attributes:
class Game:
def __init__(self, player1, player2, player3, player4, score_pair1, score_pair2):
self.player1 = player1
self.player2 = player2
self.player3 = player3
self.player4 = player4
self.score_pair1 = score_pair1
self.score_pair2 = score_pair2
self.Online_Players_Dict = []
self.Temp_Players_Dict = []
self.Temp_import_players = []
I have method inside of this class for work with all attributes:
def create_temp_players(self):
for i in ('player1', 'player2', 'player3', 'player4'):
temp = self.__getattribute__(i)
if not isinstance(temp, Player):
for ii in self.Temp_import_players:
if ii['name'] == self.__getattribute__(i):
self.__setattr__(i, Player(self.__getattribute__(i), ii['score'], ii['daily_score']))
b = self.__getattribute__(i)
a = dict(name=b.name, score=b.score, daily_score=b.daily_score)
self.Online_Players_Dict.append(a)
temp = self.__getattribute__(i)
if not isinstance(temp, Player):
self.__setattr__(i, Player(self.__getattribute__(i)))
b = self.__getattribute__(i)
a = dict(name=b.name, score=b.score, daily_score=b.daily_score)
self.Online_Players_Dict.append(a)
self.Temp_Players_Dict.append(a)
And everything work correct, but I think not right to use (getattribute) and (setattr) here. But I can't find other way to correct interaction attributes in the code.
Do you have any idea how to make it better?
PS: Class Player also has attribute:
class Player:
def __init__(self, name, score=500, daily_score=0):
self.name = name
self.score = score
self.daily_score = daily_score
I changed code like this
for player, i in zip(self.players, (0, 1, 2, 3)):
if not isinstance(self.players[i], Player):
for ii in self.Temp_import_players:
if ii['name'] == self.players[i]:
self.players[i] = Player(self.players[i], ii['score'], ii['daily_score'])
a = dict(name=self.players[i].name, score=self.players[i].score, daily_score=self.players[i].daily_score)
self.Online_Players_Dict.append(a)
if not isinstance(self.players[i], Player):
self.players[i] = Player(self.players[i])
a = dict(name=self.players[i].name, score=self.players[i].score, daily_score=self.players[i].daily_score)
self.Online_Players_Dict.append(a)
self.Temp_Players_Dict.append(a)
Later will refactoring all self.playerX to self.player[X]
I am using eval to run a generated string to append the newly created EggOrder instance to the list of the correct instance of the DailyOrders class. The day provided by EggOrder is used to used to append to the correct instance. This relies on eval and the variable name of the DailyOrders instance and so it would be great to get this removed. I know there must be a better way.
class DailyOrders:
PRICE_PER_DOZEN = 6.5
def __init__(self, day):
self.orders = []
self.day = day
def total_eggs(self):
total_eggs = 0
for order in self.orders:
total_eggs += order.eggs
return total_eggs
def show_report(self):
if self.total_eggs() < 0:
print("No Orders")
else:
print(f"Summary:\nTotal Eggs Ordered: {self.total_eggs()}")
print(f"Average Eggs Per Customer: {self.total_eggs() / len(self.orders):.0f}\n*********")
class EggOrder():
def __init__(self, eggs=0, name="", day=""):
if not name:
self.new_order()
else:
self.name = name
self.eggs = eggs
self.day = day
eval(f"{self.day.lower()}.orders.append(self)")
def new_order(self):
self.name = string_checker("Name: ")
self.eggs = num_checker("Number of Eggs: ")
self.day = string_checker("Date: ")
def get_dozens(self):
if self.eggs % 12 != 0:
dozens = int(math.ceil(self.eggs / 12))
else:
dozens = self.eggs / 12
return dozens
def show_order(self):
print(f"{self.name} ordered {self.eggs} eggs. The price is ${self.get_dozens() * DailyOrders.PRICE_PER_DOZEN}.")
if __name__ == "__main__":
friday = DailyOrders("Friday")
friday_order = EggOrder(12, "Someone", "Friday")
friday_order.show_order()
friday.show_report()
saturday = DailyOrders("Saturday")
saturday_order = EggOrder(19, "Something", "Saturday")
saturday_order = EggOrder(27, "Alex Stiles", "Saturday")
saturday.show_report()
DailyOrders isn't actually a superclass (it was in a earlier version), it acts like one and I suspect the answer might have some inheritance.
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()
I'm new to OOP and am practicing putting together a little bit more complex programs using various classes and implementing principles like Inheritance. I've created a Ping-Pong simulation that has a Player class which contains the probability that the player will win their serve. Then I have a PingPong class which is a subclass of the super class RacquetSports. Each instance is a single game, with the ability to change server, and record who won, and whether or not it was a shut-out. Finally, I have a SimStats class whose purpose is to record the stats across "n" number of games.
My problem is that it seems like my play_game function is not correctly firing, when I place a print statement in there it never triggers. My current result when running the whole program is that Player1 has 10 wins no shutouts, and Player2 has zero of both.
Finally, any suggestions on better OO practice would also be appreciated. Here is my player class:
from random import random
class Player(object):
def __init__(self, prob_win):
self.prob = prob_win
self.points = 0
def wins_serve(self):
return self.prob >= random()
def add_point(self):
self.points += 1
def get_score(self):
return self.points
My RacquetSports class:
from abc import ABCMeta, abstractmethod
from player import Player
class RacquetSport(object):
__metaclass__ = ABCMeta
def __init__(self, prob1, prob2):
self.player1 = Player(prob1)
self.player2 = Player(prob2)
self.server = self.player1
def play_game(self):
while not self.game_over():
self.sim_point()
#abstractmethod
def type(self):
pass
def chg_server(self):
if self.server == self.player1:
self.server = self.player2
else:
self.server = self.player1
def sim_point(self):
if self.server.wins_serve():
self.server.add_point()
else:
self.chg_server()
#abstractmethod
def game_over(self):
pass
def get_scores(self):
return self.player1.get_score(), \
self.player2.get_score()
def return_stats(self):
p1_score, p2_score = self.get_scores()
print(p1_score, p2_score)
won = 'p1'
if p2_score > p1_score:
won = 'p2'
return won, self.__shutout(p1_score, p2_score)
#staticmethod
#abstractmethod
def __shutout(score1, score2):
pass
My PingPong and SimStats classes, as well as my calling code:
from racquet import RacquetSport
class PingPong(RacquetSport):
def type(self):
return 'Ping Pong'
def game_over(self):
return self.player1.get_score == 11 or \
self.player2.get_score == 11
#staticmethod
def __shutout(score1, score2):
return abs(score1 - score2) == 11
class SimStats(object):
def __init__(self):
# First field is games won, second is shutouts.
self.gms_won_p1 = [0] * 2
self.gms_won_p2 = [0] * 2
def update(self, game):
won, shutout = game.return_stats()
if won == 'p1':
self.gms_won_p1[0] += 1
if shutout:
self.gms_won_p1[1] += 1
else:
self.gms_won_p2[0] += 1
if shutout:
self.gms_won_p2[1] += 1
def print_results(self):
tot_games = self.gms_won_p1 + self.gms_won_p2
print('Wins for Player 1 = {} Shutouts = {}\n'
'Wins for Player 2 = {} Shutouts = {}'.format(*tot_games))
if __name__ == '__main__':
stats = SimStats()
for x in range(1, 11):
game = PingPong(.5, .5)
stats.update(game)
stats.print_results()
Your first problem is that you never call play_game. My guess is that you intend it to work like this:
if __name__ == '__main__':
stats = SimStats()
for x in range(1, 11):
game = PingPong(.5, .5)
game.play_game()
stats.update(game)
stats.print_results()
Next, you have a bug that will cause the entire game to last forever. Take a look at these lines:
def game_over(self):
return self.player1.get_score == 11 or \
self.player2.get_score == 11
get_score is a function, so you need to call it:
def game_over(self):
return self.player1.get_score() == 11 or \
self.player2.get_score() == 11
I'm trying to simulate a game of Counter Strike. Basically I have two teams with different players (all the players are identical for now) and I want them to "fight" and when all the players on one team are dead, the simulation should end.
I'm trying to understand why the simulation I'm running never ends. I feel like I'm misunderstanding some core element of simpy but I don't really know what.
All of the process and simpy related code are in main.py and player.py.
I'm trying to get my simulation to end once every player has "died".
Basically I want every player to be a process that constantly checks their surrounding area (the node they are in which is represented by the Hotspot class) to see if there are any enemies. If there are any enemies they will choose one at random and "attack" them. Once all of the players from any team have health below 0 the simulation should end and the team that won should increment their win count by 1.
EDIT: Also of note, when I ran it through pdb it seemed like none of the player's health were decreasing and that the play method wasn't being run.
EDIT 2: I don't think all of the code needs to be read to find the problem, I think it's mostly in the main and player files but I'm not 100% sure because the code loops infinitely without error
Here is my code
main.py
from player import Player
from game_map import Game_Map
from team import Team
from sides import Sides
import simpy
import pdb
def main():
team_a = Team("Team_A", Sides.CT)
team_b = Team("Team_B", Sides.T)
gmap = Game_Map()
gmap.spawn_team(team_a)
gmap.spawn_team(team_b)
env = simpy.Environment()
for team in (team_a, team_b):
for player in team.players:
env.process(player.play(env))
env.run(until=round(team_a, team_b, env))
def round(team_a, team_b, env):
while True:
if team_a.all_dead():
team_b.round_wins += 1
print team_b
env.exit()
if team_b.all_dead():
team_a.round_wins += 1
print team_a
env.exit()
if __name__ == "__main__":
main()
player.py
import simpy
from sides import Sides
import numpy as np
import pdb
class Player(object):
""" Class that represents a CSGO player"""
def __init__(self, steam_id, team, acc, hs_percentage):
# the player's id
self.steam_id = steam_id
# percentage of shots that hit, accuracy
self.acc = acc
# percentage of hits that hit the head
self.hs_percentage = hs_percentage
# the team
self.team = team
# the player's health, this changes when the teams "fight"
self.health = 100
# the current hotspot that the player is in
self.current_location = 0
# if the player is alive or dead
self.is_alive = True
def play(self, env):
"""Process that simulates the player's actions. This is run once every round until
the round is over"""
while(self.is_alive):
target = self.choose_target()
if target == -1:
continue
yield env.timeout(5)
else:
target.inflict_self(self.determine_damage())
yield env.timeout(5)
def determine_damage(self):
"""The amount of damage the player will inflict on the enemy"""
return 27
def choose_target(self):
"""Choose a target to attack from the enemies in the hotspot"""
# 1 - side converts 0 to 1 and 1 to 0
enemy_list = self.current_location.players[1 - self.team.side]
num_enemies = len(enemy_list)
# if there are no enemies currently in the same location of the player
# simply return 0
if num_enemies == 0:
return -1
# pick an enemy randomly from the list of enemies and return their object
return enemy_list[np.random.random_integers(0, num_enemies - 1)]
def get_side(self):
return self.team.side
def inflict_self(self, damage):
"""Inflict damage onto own class. If damage moves health below 0, mark the
player as "Dead" and remove them from the map"""
self.health = self.health - damage
if self.health <= 0:
self.current_location.players[self.team.side].remove(self)
self.is_alive = False
def __str__(self):
return "Steam id: {0}\tIs Alive: {1}\tCurrent Location: {2}".format(self.steam_id, self.is_alive, self.current_location)
def tests():
return
if __name__ == "__main__":
tests()
game_map.py
import networkx as nx
from hotspot import Hotspot
import matplotlib.pyplot as plt
class Game_Map(object):
""" Generic map that represents general outline of all counter strike maps"""
def __init__(self):
self.graph = nx.Graph()
self.spawns = [Hotspot()]
self.graph.add_node(self.spawns[0])
def add_team(team):
#side = team.side
# side is 0 because for testing the simulation
# we are only using one node
side = 0
for player in team.players:
self.spawns[side].move_into(player)
def spawn_team(self, team):
for player in team.players:
self.spawns[0].move_into(player)
def draw(self):
nx.draw(self.graph)
plt.show()
def tests():
"""Tests to see that Game_Map class works properly"""
# initialize the map
gmap = Game_Map()
gmap.draw()
# if this module is being run explicitly from the command line
# run tests to assure that this module is working properly
if __name__ == "__main__":
tests()
hotspot.py
import simpy
from player import Player
from team import Team
from sides import Sides
class Hotspot(object):
"""Hotspots are the representation for different areas of the map. This is where players 'fight'."""
def __init__(self):
self.players = [[], []]
def move_into(self, player):
side = player.get_side()
self.players[side].append(player)
player.current_location = self
return 1
def __eq__(self, other):
return id(self) == id(other)
def __hash__(self):
return id(self)
def tests():
"""Tests to see that hotspot works properly"""
hotspot_list = []
for i in range(5):
hotspot_list.append(Hotspot())
for spot in hotspot_list:
team_a = Team("team_a", Sides.CT)
team_b = Team("team_b", Sides.T)
spot.move_into(Player(1, team_a, .5, .5))
spot.move_into(Player(1, team_b, .5, .5))
print "Hotspot id = {0}".format(id(spot))
for team in spot.players:
for player in team:
print "player = {0} in team {1}".format(player, player.team)
if __name__ == "__main__":
tests()
sides.py
class Sides(object):
"""Enum object, simply represents CT (Counter Terrorists) as 0 and
T (Terrorists) as 1"""
CT, T = range(2)
team.py
from player import Player
class Team(object):
"""Class that holds critical team information"""
def __init__(self, name, side):
self.round_wins = 0
self.players = []
self.name = name
self.side = side
self.generate_team()
def all_dead(self):
count = 0
for player in self.players:
if player.is_alive == False:
count += 1
if count == 5:
return True
else:
return False
def __str__(self):
rep = "Team: {0}, Round Wins: {1}\n".format(self.name, self.round_wins)
for player in self.players:
rep += player.__str__() + '\n'
return rep
def generate_team(self):
for i in range(5):
self.players.append(Player(1, self, .5, .2))
__rep__ = __str__
requirements.txt
decorator==3.4.2
matplotlib==1.4.3
mock==1.0.1
networkx==1.9.1
nose==1.3.6
numpy==1.9.2
pyparsing==2.0.3
python-dateutil==2.4.2
pytz==2015.2
scipy==0.15.1
simpy==3.0.7
six==1.9.0
Your round() function is the culprit:
env.run(until=round(team_a, team_b, env))
def round(team_a, team_b, env):
while True:
if team_a.all_dead():
team_b.round_wins += 1
print team_b
env.exit()
if team_b.all_dead():
team_a.round_wins += 1
print team_a
env.exit()
The function contains an infinite loop without any yields. This means it never returns and env.run() isn’t even executed.
I have a feeling this might be an infinite loop:
while(self.is_alive):
target = self.choose_target()
if target == -1:
continue
yield env.timeout(5)
You probably want to yield before the continue (which is unneeded anyway).