Share datastructures between two classes by importing - python

I have 2 classes: Vehicle & Car.
Vehicle Class has a dictionary of Car objects & a heap.
ClassV.py:
from ClassC import Car
import heapq
class Vehicle:
MapOfCars_ID = {}
heap = [] # Stores the load factor of each car
counter = 0
def createCar(number, idnum):
C = Car(number, idnum) # Create a car object
self.MapOfCars_ID[counter] = C # Dict of Car_ID : Car Object
self.heapq.heappush(heap, (0.0, counter)) # Heap stores load factor, Car_ID
counter += 1
def AssignCar():
t = heapq.heappop(heap)
MapOfCars_ID[t[1]].addPassenger()
ClassC.py is the logic for creating a Car:
from ClassV import Vehicle
class Car:
size = 0;
occupiedSeats = 0
carId = -1
def __init__(size, id_number):
self.size = size
self.carId = id_number
print "Created Car with size " + self.size + " and ID number "+ self.carId
def addPassenger():
if self.occupiedSeats < self.size:
self.occupiedSeats += 1
# Code below adjusts the load factor of the car in the heap when a passenger is added to the car
# Load factor = seat-occupied/total-seats-in-the-car
for index, value in Vehicle.heap:
if value[1] == self.carId:
Vehicle.heap[index] = heap[-1]
heap.pop()
t = (float(self.occupiedSeats/self.size), self.carId)
heap.append(t)
heapq.heapify(Vehicle.heap)
break
else:
print "Car is full!"
The program is run from another file, main.py:
from ClassV import Vehicle
from random import randint
def main():
for i in range(1, 10): # Create 10 cars
r = randint(1,6) # Maximum number of seats could be 6 in a car
Vehicle.createCar(r, i) # <Car size, ID>
Vehicle.AssignCar()
if __name__ == "__main__":
main()
The intention of this program is to create 10 cars and then assign passengers to the car having minimal occupancy.
As shall be evident from the program, The heap which is a class attribute of the class Vehicle is being updated in Car Class. And, Class Vehicle is creating an array of Car objects.
This gives me an error:
File "/home/Testing/ClassC.py", line 1, in <module>
from ClassV import Vehicle
ImportError: cannot import name Vehicle
I have searched around but could really find a resolution to this problem. What is the right way to resolve this problem?
Update:
I got a few comments which explain that this is possibly a problem of circular imports and has 2 solution:
Refactor the program to avoid circular imports
Move the imports to the end of the module
I am looking for feedback as to how do I do either of these.

Update: I got a few comments which explain that this is possibly a problem of circular imports and has 2 solution:
Refactor the program to avoid circular imports
Move the imports to the end of the module
Several things wrong here:
Your chosen nomenclature is confusing/wrong. Why is the Vehicle class a container for Car instances? I would call it something like VehicleRegistry, or similar, to make its intent explicit.
You have a fundamental design flaw, in that you are violating the responsibilities of your two classes. An instance of Car should be able to stand in isolation, and when I add a passenger to it, it should only affect the internal state of that instance, it should not affect the state of Vehicle, this is a recipe for fragile code, that breaks easily.
Do not use class level attributes, unless you know exactly what you're doing. Altering the state of a class level attributes can alter the state of said attribute for all instances of that class, leading to some very interesting and unexpected behaviour.
This is what I mean by a class level attributes:
class Person(object):
first_name = "Bob"
last_name = "Smith"
These are tied to the class, not an instance.
Possible solution:
Herewith some code to illustrate what I mean:
Your addPassenger method should only add a passenger to the car and return whether it was successful or not, nothing else.
def add_passenger(self) -> bool:
if self.capacity > self.number_of_passengers:
self.capacity = self.capacity + 1
return True
return False
You place the updating of your load factor logic in the assign_car method, for example:
def assign_car(self):
car_id = heapq.heappop(self.heap)
car = self.vehicle_registry[car_id]
result = car.add_passenger()
if result:
# Put your load factor update logic here ...
print("A passenger was successfully added to: {0}".format(car.id_number))
else:
print("A passenger could not be added to the car.")
Edit[2018/09/24]:
Alternatively, if load factor is an attribute of the Car, then it makes sense to place it on the instance of car itself, and allow the VehicleRegistry to consume the load factor state.

Related

How to call a function when instances are being dynamically created?

I'm a Java newbie, transitioning into Python and I've been trying to develop a simplistic market-like interaction between companies and investors. Basically, there are 100 companies and 100 investors been created dynamically with random values at initialization.
class Company:
def __init__(self):
""" Constructor for the Company object """
self.id: str = uuid.uuid4().hex
self.shares_num = random.randint(500, 1000)
self.share_price = Decimal(random.randint(10, 100))
class Investor:
def __init__(self):
""" Constructor for the Investor object """
self.id_inv: str = uuid.uuid1().hex
self.budget: Decimal = random.randint(50_000, 100_000)
self.acq_shares = 0
def sell_shares(self):
trade = self.budget - Company.get_share_price
Company.get_shares_num -= 1
self.acq_shares += 1
self.set_budget = trade
return self
The function sell_shares() performs the actual sale, i.e. makes the trade between the investor's money and the company's shares.
The companies and investors are then placed into a list with their ID and a dict with share's data for companies and budget's for investors.
# dynamic instances of Companies and Investors
comp = [Company() for _ in range(10)]
companies = {c.id: c for c in comp}
inv = [Investor() for _ in range(10)]
investors = {i.id_inv: i for i in inv}
The problem is when I call the sell_shares() method Investors.sell_shares neither the instances of companies or shares are affected. I don't really see anything wrong, apart from the fact that it isn't working.
Any suggestions on how I could try and fix this?
Thanks a million :)
P.S.: I'm using Python 3.8.2 with standard libraries.
Probably the biggest source of your pain is your use of mutable global variables inside of a class method. Please don't do that. Every time you do, somewhere, a baby kitten dies.
You could fix this problem quite easily by making trade() accept a keyword argument for a company, and then just operate using self (referring to that particular investor), and the keyword argument, (referring to a particular instance of a company). Don't try to access all the investors and companies at once, being that your method only depends on one of each.
import uuid
import random
from decimal import Decimal
class Company:
def __init__(self):
""" Constructor for the Company object """
self.id: str = uuid.uuid4().hex
self.shares_num = random.randint(500, 1000)
self.share_price = Decimal(random.randint(10, 100))
class Investor:
def __init__(self):
""" Constructor for the Investor object """
self.id: str = uuid.uuid1().hex
self.budget: Decimal = random.randint(50000, 100000)
self.acq_shares = 0
def trade(self, company):
trading = self.budget - company.share_price
company.share_price -= 1
self.acq_shares += 1
# this equation is wrong, but I'm not sure entirely what you want to do here
# this is equivalent to -company.share_price
company.share_price = trading - self.budget
self.budget = trading
return self.id, self.budget
Note that this also removes any need to create get_share_price() or get_budget() methods.
If, somehow, I've misunderstood your setup, and your trade function does depend on all investors and all companies at once, then don't make it a class method. Make it a regular function, outside of a class, where it takes two arguments like so def trade(investors, companies):, because in that case, it doesn't have to do with that single class instance of investor.
You can test out your classes like so:
# Generate companies and investors
comp = [Company() for _ in range(10)]
companies = {c.id: c for c in comp} # <-- not sure what this is used for
inv = [Investor() for _ in range(10)]
investors = {i.id: i for i in inv} # <-- not sure what this is used for
# Select an investor and a company
some_investor = inv[random.randint(0,9)]
some_company = comp[random.randint(0,9)]
# Make a trade
some_investor.trade(some_company)
print(some_company.share_price) # prints a negative number
Note that your current formula for share price seems very wrong to me, being that it's equivalent to self.budget - company.share_price - self.budget.
Hope this helps.

(Classname) has no attribute (attribute)

I'm trying to create a DnD style dungeon crawler game. I'm using the 5E SRD and other publicly available information as the base for my characters and gameplay.
Currently I'm working on the character generator, and it seems to be going well, but I've hit a roadblock when trying to assign the racial bonuses. I've got the races set up as their own subclasses, each with it's unique bonuses. When I try to assign the appropriate bonuses based on the character's race I get a (Classname)has no attribute (attribute) error.
python
class Race:
def __init__(self, race):
self.name = race
self.racial_str_bonus = 0
self.racial_char_bonus = 0
class Dragonborn(Race):
def __init__(self):
super()
self.name = "Dragonborn"
self.racial_str_bonus = +2
self.racial_char_bonus = +1
def get_racial_bonus(race):
race = race
racial_str_bonus = 0
racial_char_bonus = 0
if race == "Dragonborn":
racial_str_bonus = Dragonborn.racial_str_bonus
racial_char_bonus = Dragonborn.racial_char_bonus
return racial_str_bonus, racial_char_bonus
class BaseCharacter:
def __init__(self, racial_str_bonus, racial_char_bonus):
self.racial_str_bonus = racial_str_bonus
self.racial_char_bonus = racial_char_bonus
#classmethod
def generate_player_character(cls):
cls.race = input("Race: ")
get_racial_bonus(cls.race)
BaseCharacter.generate_player_character()
What I'm looking for is something along the line of:
'''
Race: Dragonborn
print(my_player_char.racial_str_bonus)
2
'''
Where am I goofing up?
Thanks, everyone for the feedback. In cleaning up the code to get it minimally reproducible, I figured out the issue. Per Jonrsharpe's note, I corrected the inhertance invocation to 'super().init(self)'. Once that was correct, I realized that the way they had been defined, I had to include parentheses in the property call: "Dragonborn().racial_str_bonus".
Thanks again, and I will remember to improve my submissions in the future.

Why is it so that I keep getting the error that Driver sponsor is not defined, even though I placed it within a class?

I have been trying my hand at a NASCAR project, where I would have to use a class to create 20 unique vehicles and then have them race ( or to see who would reach 500 miles first, through the means of repeatedly choosing a different speed between 1 and 120 and adding it to an increasing odometer). I made what you see below and ran it, and it boots well into the Python IDLE. However, it will always tell me that NameError: name 'Driver_sponsor' is not defined. See, I have been facing this error for a while now, and I have tried placing the Driver_sponsor list into a class, placing it into the Main def and placing the keyword self. before it. No matter what I did, I faced this error. I am going to go back into my class book to see what I can do, but I am hoping that someone here can tell me what I am missing within my code, since, really, I am extremely lost.
from random import randint
import time
class Car:
def __init__(self,Driver_Name,Sponsor):
self.__Total_Odometer_Miles = 0
self.__Speed_Miles_Per_Hour = 0
self.__Driver_Name = Driver_Name
self.__Sponsor = Sponsor
self.__Driver = ('Drivers name Missing')
self.__Sponsor = ('Sponsor Missing')
self.__Driver_sponsor = {'A.J.Allmendinger:3M','Aric Almirola:Allegiant ','Trevpr Bayne:AMR ','Ryan Blaney:Camping World ','Clint Bowyer:Chevrolet ',
'Chris Buesher:Coca-Cola','Kurt Busch:Coca-light ','Kyle Busch:Credit One ','Landon Cassill:Ford','Matt DiBenedetto:FDP',
'Austin Dillon:','Ty Dillon:','Dale Earnhardt:Jacob Companies ','Chase Elliott: M & M ','Denny Hamlin: Microsoft ',
'Kevin Harvick:GoodYear ','Jimmie Johnson:Nationwide','Erik Jones:SUNOCO','Kasey Kahne:Toyota','Matt Kenseth:Visa ' }
def Name(self,Driver_Name):
self.__Driver_Name = Driver_Name
def Retrieve_Name(self):
return self.__Driver_Name
def __mutualize__(self):
self.__Total_Odometer_Miles = 0
self.__Speed_Miles_Per_Hour = 0
def sponsors(self):
self.__Driver_sponsor = {'A.J.Allmendinger:3M','Aric Almirola:Allegiant ','Trevpr Bayne:AMR ','Ryan Blaney:Camping World ','Clint Bowyer:Chevrolet ',
'Chris Buesher:Coca-Cola','Kurt Busch:Coca-light ','Kyle Busch:Credit One ','Landon Cassill:Ford','Matt DiBenedetto:FDP',
'Austin Dillon:','Ty Dillon:','Dale Earnhardt:Jacob Companies ','Chase Elliott: M & M ','Denny Hamlin: Microsoft ',
'Kevin Harvick:GoodYear ','Jimmie Johnson:Nationwide','Erik Jones:SUNOCO','Kasey Kahne:Toyota','Matt Kenseth:Visa ' }
def Retrieve_sponsor(self,Driver_sponsor):
return self.__Driver_sponsor
def main():
for key in Driver_sponsor():
CurrentCar = Car()
CurrentCar.Driver = key
CurrentCar.Sponsor = val
CurrentCar.MPH = randint(1,120)
time.sleep(.05)
time = 5
currentCar.ODT = 5
CurrentCar.ODT = CurrentCar.ODT + CurrentCar.MPH*Time
print(CurrentCar.Driver,CurrentCar.ODT)
if CurrentCar.ODT >= 500:
print('\ the winner is',key,'t\ sponsored by',val)
main()
There are a few issues with your code.
First, you're getting this error because you're calling a variable that isn't set.
But more importantly, you're trying to access the driver-sponsor dict before you've initialized an instance of Car (which currently only happens inside the loop that iterates over Driver_sponsor!).
If you want to loop over driver-sponsor pairs and initialize a new Car for each one, then do you really need the full Driver_sponsor dict initialized for every Car? If so, just pass it as an argument when constructing Car and populate self.__Driver_sponsor.
For example:
driver_sponsor_pairs = {'A.J.Allmendinger:3M',...,'Matt Kenseth:Visa'}
class Car:
def __init__(self, driver_sponsor):
# ...
self.driver_sponsor = driver_sponsor
CurrentCar = Car(driver_sponsor=driver_sponsor_pairs)
# now refer to CurrentCar.driver_sponsor
Second, you are only asking for key when looping over the Driver_sponsor dict, but you call on both key (for Driver) and val (for Sponsor) in each loop . Extract both key and val in your loop creation. You'll need the .items() dict method to get both values:
for key, val in driver_sponsor_pairs.items():
...
Third, your Car __init__ expects Driver and Sponsor arguments, but you try to define CurrentCar = Car() and then populate CurrentCar.Driver and CurrentCar.Sponsor afterwards. Continuing with the above updates, try instead:
CurrentCar = Car(Driver=key, Sponsor=val)
Fourth, you won't need the Retrieve_sponsor() method if you already have the .Sponsor attribute set.
There are a lot of misunderstandings here about Object syntax and design. You may find it frustrating to try and debug at this level of complexity. I would suggest starting very simply, say, with Car() having just one attribute. Test that, make sure it works as you want, and then build more attributes and methods from there.

Python: Pokemon battle (classes, functions)

I just started learning python and I am hoping you guys can help me comprehend things a little better. If you have ever played a pokemon game for the gameboy you'll understand more as to what I am trying to do. I started off with a text adventure where you do simple stuff, but now I am at the point of pokemon battling eachother. So this is what I am trying to achieve.
Pokemon battle starts
You attack target
Target loses HP and attacks back
First one to 0 hp loses
Of course all of this is printed out.
This is what I have for the battle so far, I am not sure how accurate I am right now. Just really looking to see how close I am to doing this correctly.
class Pokemon(object):
sName = "pidgy"
nAttack = 5
nHealth = 10
nEvasion = 1
def __init__(self, name, atk, hp, evd):
self.sName = name
self.nAttack = atk
self.nHealth = hp
self.nEvasion = evd
def fight(target, self):
target.nHealth - self.nAttack
def battle():
print "A wild appeared"
#pikachu = Pokemon("Pikafaggot", 18, 80, 21)
pidgy = Pokemon("Pidgy", 18, 80, 21)
pidgy.fight(pikachu)
#pikachu.fight(pidgy)
Full code here: http://pastebin.com/ikmRuE5z
I am also looking for advice on how to manage variables; I seem to be having a grocery list of variables at the top and I assume that is not good practice, where should they go?
If I was to have fight as a instance method (which I'm not sure I would), I would probably code it up something like this:
class Pokemon(object):
def __init__(self,name,hp,damage):
self.name = name #pokemon name
self.hp = hp #hit-points of this particular pokemon
self.damage = damage #amount of damage this pokemon does every attack
def fight(self,other):
if(self.hp > 0):
print("%s did %d damage to %s"%(self.name,self.damage,other.name))
print("%s has %d hp left"%(other.name,other.hp))
other.hp -= self.damage
return other.fight(self) #Now the other pokemon fights back!
else:
print("%s wins! (%d hp left)"%(other.name,other.hp))
return other,self #return a tuple (winner,loser)
pikachu=Pokemon('pikachu', 100, 10)
pidgy=Pokemon('pidgy', 200, 12)
winner,loser = pidgy.fight(pikachu)
Of course, this is somewhat boring since the amount of damage does not depend on type of pokemon and isn't randomized in any way ... but hopefully it illustrates the point.
As for your class structure:
class Foo(object):
attr1=1
attr2=2
def __init__(self,attr1,attr2):
self.attr1 = attr1
self.attr2 = attr2
It doesn't really make sense (to me) to declare the class attributes if you're guaranteed to overwrite them in __init__. Just use instance attributes and you should be fine (i.e.):
class Foo(object):
def __init__(self,attr1,attr2):
self.attr1 = attr1
self.attr2 = attr2v
You don't need the variables up the top. You just need them in the init() method.
The fight method should return a value:
def fight(self, target):
target.nHealth -= self.nAttack
return target
You probably want to also check if someone has lost the battle:
def checkWin(myPoke, target):
# Return 1 if myPoke wins, 0 if target wins, -1 if no winner yet.
winner = -1
if myPoke.nHealth == 0:
winner = 0
elif target.nHealth == 0:
winner = 1
return winner
Hope I helped.
I am only going to comment on a few obvious aspects, because a complete code review is beyond the scope of this site (try codereview.stackexchange.com)
Your fight() method isn't saving the results of the subtraction, so nothing is changed. You would need to do something like this:
def fight(target, self):
target.nHealth -= self.nAttack
# check if target is dead now?
I might even recommend not imposing a modification on your target directly. It may be better if you can call an attack(power) on your target, and let it determine how much damage is done. You can then check if the target is dead yet. Ultimately I would think you would have some "dice" object that would determine the outcomes for you.
As for globals... just stop using them. It is a bad habit to have them unless you really have a good reason. Have functions that return results to the caller, which you then make use of:
def func(foo):
return 'bar'
You can however have a module of constants. These are a bunch of values that don't change for the life of the application. They are merely variables that provide common values. You might create a constants.py and have stuff like:
UP = "up"
DOWN = "down"
DEAD = 0
...
... And in your other modules you do:
from constants import *

Class does not seem to be Global in python

I setup a class and it accepts and prints out the variables fine in one if statement.
class npc: #class for creating mooks
def __init__(self, name):
self.name = name
def npc_iq (self,iq):
self.iq = []
def npc_pp (self,pp):
self.pp = []
def npc_melee (self, melee):
self.melee = []
def npc_ct (self, ct):
self.ct = []
It works fine in this if statement
if menu_option == 1:
print "Choose melees for npc"
init_bonus = random.randint(0,2)
char_PP = random.randint(7,15)
char_iq = random.randint(7,15)
npc_Melees = int(raw_input(prompt))
combat_time = math.floor((round_attacks - init_bonus - math.floor(char_PP/2) - math.floor(char_iq/2)) / npc_Melees)
#function for calculating sequence number
print "combat time is"
print combat_time
mook = "mook%s" % counter # adds different mook names to program
mook = npc(mook)
mook.iq = (char_iq)
mook.pp = (char_PP)
mook.melee = (npc_Melees)
mook.ct = (combat_time)
counter += 1
But on this statement it will print out the name in the class but not ct.
elif menu_option ==4:
print "Printing out all mooks"
print
printcount = counter -1
while printcount != 0:
mookprint = "mook%s" % printcount
mookprint = npc(mookprint)
print mookprint.name
print mookprint.ct
print
printcount -= 1
Why would a mookprint have any idea what value ct should be? The constructor for npc initialises a new instance of npc, with the name given as a parameter, but ct is left empty.
When you create an NPC in menu option 1, you do not create a global instance of npc. If you want to refer to a previously created instance of npc, you will need to find some way of storing them. Dictionaries may be a good solution for you. A dictionary is an object that holds mappings between keys and values. If you know the key, then you can find the assosicated value. In this case you would make name the key and the value the npc instances.
eg.
npcsDict = dict()
if menu_option == 1:
# code for intialising a new instance of npc
...
# most, if not all of the initialisation code should be moved to the
# __init__ method for npc
# now store the newly created mook
npcsDict[mook.name] = mook
elif menu_option == 4:
print "Printing out all mooks"
print
for mookName in npcsDict:
print npcsDict[mookName].name
print npcsDict[mookName].ct
print
i dont really understand your problem.
your working example:
mook = npc(mook)
mook.iq = (char_iq)
mook.pp = (char_PP)
mook.melee = (npc_Melees)
mook.ct = (combat_time)
mook.ct is value of (combat_time)
your failing example:
mookprint = npc(mookprint)
print mookprint.name
print mookprint.ct
mookprint.ct's value is nothing because it is never set.
The elif will only be executed if the if has not, so if the elif block runs, ct was never set
I don't think you're understanding how four lines work:
mookprint = "mook%s" % printcount
mookprint = npc(mookprint)
print mookprint.name
print mookprint.ct
Every time this block of code is run, the following things are happending:
You're assigning a string of the form "mook" to the variable mookprint
You're creating a new instance of the npc class. You should note that all of the instances you're creating will be separate from eachother. This new instance will have an attribute with the name that was previously held in the variable mookprint and this instance of npc will be assigned to mookprint.
You're printing the name attribute of the instance of the npc class that you created in the previous step. This works because when this instance was created, the __init__ method of your class was called with the argument name being set to "mook1" or whatever was stored in mookprint at the time.
You're printing the ct attribute of the instance of the npc class that you just created. Since you never set the ct attribute to anything, this will not work how you expected.
If you want to count the number of instances of your npc class, you'll need to create a class attribute. This is a variable whose value is common across all instances of a class. To do so, you'll need to modify your class definition to add an item to this attribute every time you make a new instance of the class. It will look something like this:
class npc: #class for creating mooks
ct = []
def __init__(self, name):
self.name = name
self.ct.append(name)
def get_ct(self):
return len(self.ct)
With the above, the variable ct will be a list that is common to all instances of npc and will grow every time a new npc is created. Then the method get_ct will count how long this list is.
Then you'll need to modify the four lines I mentioned to look like:
mookprint = "mook%s" % printcount
mookprint = npc(mookprint)
print mookprint.name
print mookprint.get_ct()
I think the code above shows how to change your code to work more how you expected it to work. However, it should be noted that you rarely want to create classes where each instance depends on information about the other instances. It is usually a better design to do something like Dunes suggested, storing the instances in a dictionary, or some other data structure, and keeping track of them that way.

Categories

Resources