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.
Related
New to OOP and python, I am struggling enormously to grasp what good classes actually are for. I tried to ask help from a lecturer who said "oh, then you should read about general methods to classes". Been putting in a days work but get no where.
I get it that a class allow you to collect an instance structure and methods to it, like this:
class Items:
def __init__(self, item_id, item_name):
self.item_id = item_id
self.item_name = item_name
def show_list(self):
print(self.item_id, self.item_name)
idA = Items("idA", "A")
idA.show_list()
But what is even the point of a class if there were not MANY instances you would classify? If I have a method within the class, I must hard code the actual instance to call the class for. What if you want a user to search and select an instance, to then do operations to (e.g. print, compute or whatever)??
I thought of doing it like this:
class Items:
def __init__(self, item_id, item_name):
self.item_id = item_id
self.item_name = item_name
def show_list(self):
print(self.item_id, self.item_name)
idA = Items("idA", "A")
idB = Items("idB", "B")
select_item = input("enter item id")
select_item.show_list()
Replacing hard coded variable with input variable doesn't work, probably logically. I then played with the idea of doing it like this:
class Items:
def __init__(self, item_id, item_name):
self.item_id = item_id
self.item_name = item_name
iL = [Items('idA', 'A'), Items('idB', 'B')]
selected_item = input("enter item id")
for selected_item in iL:
print(f'{selected_item.item_id} {selected_item.item_name}')
Now all are called thanks to making it a list instead of separate instances, but how do I actually apply code to filter and only use one instance in the list (dynamically, based on input)?
I would love the one who brought me sense to classes. You guys who work interactively with large data sets must do something what I today believe exist in another dimension.
See examples above^^
It seems you want to find all the instances of a certain element within a class.
This is as simple as:
print([x for x in iL if x.item_id == selected_item])
Now, you may ask why you can't just store the elements of iL as tuples instead of classes. The answer is, you can, but
("idA", "A")
is much less descriptive than:
item_id = "idA"
item_name = "A"
Any code you write with classes, you should in theory be able to write without classes. Classes are for the benefit of the coder, not the end-user of the program. They serve to make the program more readable, which I'm sure you'll find is a desirable property.
Your point here is to lookup for Items instances based on their item_id attribute.
That's a thing to create instances of a class.
It's a completely different thing to search for items objects stored in memory - that is not directly linked to the concept of OOP, classes and instances.
You could use dictionary to store references of your objects and then lookup in your dictionary.
class Items:
def __init__(self, item_id, item_name):
self.item_id = item_id
self.item_name = item_name
def show_list(self):
print(self.item_id, self.item_name)
idA = Items("idA", "A")
idB = Items("idB", "B")
lookup_dict = {"idA": idA, "idB": idB}
select_item = input("enter item id")
found_item = lookup_dict.get(select_item)
if found_item:
found_item.show_list()
else:
print(f"item {select_item} not found")
class Air:
def __init__(self,supplier,delivery,ensurance):
self.supplier = supplier
self.delivery = delivery
self.ensurance = ensurance
def rate_for_custom(self):
return (self.supplier + self.delivery + self.ensurance)
class Sea:
def __init__(self,supplier,delivery,ensurance,port_tax):
self.supplier = supplier
self.delivery = delivery
self.ensurance = ensurance
self.port_tax = port_tax
def rate_for_custom(self):
return (self.supplier + self.delivery + self.ensurance + self.port_tax)
so i'm trying to write a program that calculates the import taxes in israel.
There are two types: one in the sea and one in the air
they both share the same attributes except Sea needs to be calculated with another attribute.
I'm feeling like my code is not good(i'm new to pragramming started a week ago)
is it fine to use two classes in this case? if not what is the solution (by stil using OOP because I need to practice with it)
You can move common parts to a common parent class:
class Transport:
def __init__(self,supplier,delivery,ensurance):
self.supplier = supplier
self.delivery = delivery
self.ensurance = ensurance
def rate_for_custom(self):
return (self.supplier + self.delivery + self.ensurance)
class Air(Transport):
pass
class Sea(Transport):
def __init__(self,supplier,delivery,ensurance,port_tax):
super().__init__(supplier, delivery, ensurance)
self.port_tax = port_tax
def rate_for_custom(self):
return super().rate_for_custom() + self.port_tax
As you want to learn OOP, then you can start to see the concept of inheritance. Here is an example:
# generic class
class Custom:
def __init__(self,*args):
# collect all given parameters:
self.args = args
def rate_for_custom(self):
# just sum all numbers in given parameter:
return sum(self.args)
class Sea(Custom):
def __init__(self,supplier=0,delivery=0,insurance=0, port_tax = 0):
# Call Custom class and provide all relevant parameters:
super().__init__(supplier, delivery, insurance, port_tax)
class Air(Custom):
def __init__(self,supplier=0, delivery=0, insurance=0):
# Call Custom class and provide all relevant parameters:
super().__init__(supplier, delivery, insurance )
print(Custom(100,50,25).rate_for_custom())
# 175
print(Air(supplier=100,delivery=50,insurance=25).rate_for_custom())
# 175
print(Sea(supplier=100,delivery=50,insurance=25,port_tax=25).rate_for_custom())
# 200
Customclass is doing all the job, by summing all parameters it receives in init(). You can call this class providing the values to sum :Custom(100,50,25).rate_for_custom()
Two other classes Airand Sea are inheriting from the Customclass and are just an interface. Using them allows you to use keyword arguments instead of simple arguments: Sea(supplier=100,delivery=50,insurance=25,port_tax=25) which is more friendly.
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.
I am new to python and I would like to pass an enum as an argument to a constructor, within a function.
EDIT: I am working on a program with a class that has to organize different types of data, but most of these data types can be treated the same way. This data won't be all be added at the same time or in a foreseeable order. I would therefore like to keep the same functions, and just change the way the constructor stores the data. Let's consider this simpler example:
Say I have an enum
from enum import Enum, auto
class HouseThing(Enum):
people = auto()
pets = auto()
furniture = auto()
And I have a class House that can contain some or all of those things
class House():
def __init__(self, address, people = None, pets = None,
furniture = None):
self.address = address,
if self.people is not None:
self.people = people
etc....
And now I want to have a function that makes new furbished houses, but I want to use a function that could be used for any house:
house_things = HouseThing.furniture
def make_house_with_some_house_things(neighborhood, house_things):
neighborhood.append(House(house_things.name = house_things.name))
Is there a way to do this without first testing what kind of HouseThing house_things is first? house_things.name passes a string, but I would like it to be able to use it as a keyword.
I'm not sure exactly what you are trying to achieve here, but for the sake of solving the puzzle:
First, change House to determine what it has been passed:
class House():
def __init__(self, address, *house_things):
self.address = address
for ht in house_things:
if ht is HouseThings.people:
self.people = ht
elif ht is HouseThings.pets:
self.pets = ht
elif ht is HouseThings.furniture:
self.furniture = ht
else:
raise ValueError('unknown house thing: %r' % (ht, ))
Then, change make_house_with_some_house_things to just pass the house things it was given:
def make_house_with_some_house_things(neighborhood, house_things):
neighborhood.append(House(house_things))
I am planning to design a program to track profit and loss of my stock account, then I used Python and hope to solve it in a Object Oriented way.
Code:
class PNL(object):
stock_amount = {}
def __init__(self,cash,position):
self.cash = cash
self.position = position
def buy(self,Stock,amount):
pass
def sell(self,Stock,amount):
pass
def stock_amt(self,Stock):
if Stock().symbol not in stock_amount:
stock_amount[Stock().symbol] = 0
else:
return stock_amount
class Stock():
def __init__(self,symbol,timestamp,price):
self.symbol = symbol
self.time = timestamp
self.price = price
a = PNL(0,0)
APPL = []
APPL.append(Stock('APPL',0,10))
APPL.append(Stock('APPL',1,12))
a.stock_amt('APPL')
for stock in APPL:
if stock.time == 0:
print stock.price
But this doesn't work fine, anyone has idea on that?
Firstly you need to fix the class PNL, when you declare the methods with Stock, as its an argument/parameter, you'd better choose another name, or write it in lowercase to make difference with the class Stock.
Just think you will give an instance to these methods, no need to write the type, and by the way, no need to instantiate again the class inside the method by doing Stock().symbol, you'll give an instance, or directly the attribute symbol if you prefer.
Also, the stock_amount can be stored as a instance attribute, as below :
class PNL(object):
def __init__(self,cash,position):
self.cash = cash
self.position = position
self.stock_amount = {}
def buy(self,stock,amount):
pass
def sell(self,stock,amount):
pass
def stock_amt(self,stock):
if stock.symbol not in self.stock_amount:
self.stock_amount[stock.symbol] = 0
else:
return self.stock_amount
Then when you call your classes, i think you wanted to loop on the list APPL you've built (then just call a.stock_amt(stock_object_created) :
a = PNL(0,0)
APPL = []
APPL.append(Stock('APPL1',0,10))
APPL.append(Stock('APPL2',1,12))
for stock in APPL:
a.stock_amt(stock)
if stock.time == 0:
print stock.price
print a.stock_amount
#>>>10
#>>>{'APPL2': 0, 'APPL1': 0}