In this code I'm trying to create a portfolio object that has a method that will generate a new instance of the class Stock whenever a purchase is made. I would like that Stock object to have its ticker as its name/pointer. So I actually would like it to be interpreted as AMZN = Stock()
below, but I can't make this work. The output should be 3000. I have tried different methods with no success so would be grateful for some advice. Quite new at this so might be some complications I'm not aware of..
class Stock:
def __init__(self, ticker, price, amount):
self.ticker = ticker
self.price = price
self.amount = amount
# Create portfolio to hold stocks and available funds
class Portfolio:
def __init__(self, funds):
self.funds = funds
self.stockPortfolio = []
# Buying a stock and adding it to the portfolio
def buyStock(self, ticker, price, amount):
#Add stock to portfolio
self.stockPortfolio.append(ticker)
ticker = Stock(ticker, price, amount) # Would like this to be read as AMZN = Stock()
return
p = Portfolio(100000)
p.buyStock("AMZN", 3000, 20)
print(AMZN.amount)
CodePudding user response:
Wouldn't you rather have AMZN
be a member of the Portfolio
object that you created? Consider the scenario where you have multiple portfolios all of which own AMZN stock. Whose AMZN holdings should AMZN.amount
refer to?
Also, say I buy 20 shares of AMZN at 3000, and 20 more at 1500, Which of these purchases should it refer to? Should it instead give you a list of Stock
objects for that ticker?
I suggest you implement Portfolio.stockPortfolio
as a collections.defaultdict
object. Then, you can define a __getitem__()
method of Portfolio
to take the ticker symbol and return the correct list from stockPortfolio
. If you really want to allow access to the portfolio's holdings using attributes like p.AMZN
, you could define the __getattr__()
method:
import collections
class Stock:
def __init__(self, ticker, price, amount):
self.ticker = ticker
self.price = price
self.amount = amount
def __repr__(self):
return f"Stock({self.ticker}, {self.price}, {self.amount})"
# Create portfolio to hold stocks and available funds
class Portfolio:
def __init__(self, funds):
self.funds = funds
self.stockPortfolio = collections.defaultdict(list)
# Buying a stock and adding it to the portfolio
def buyStock(self, ticker, price, amount):
#Add stock to portfolio
stk = Stock(ticker, price, amount)
self.stockPortfolio[ticker].append(stk)
def __getitem__(self, ticker):
return self.stockPortfolio[ticker]
def __getattr__(self, attr):
if attr in ("funds", "stockPortfolio"):
return self.__getattribute__(attr)
return self.stockPortfolio[attr]
I added a __repr__
to Stock
so that we can see what the stock objects in the lists contain.
p = Portfolio(100000)
p.buyStock("AMZN", 3000, 20)
print(p["AMZN"])
# Output: [Stock(AMZN, 3000, 20)]
p.buyStock("AMZN", 3500, 10)
print(p.AMZN)
# Output: [Stock(AMZN, 3000, 20), Stock(AMZN, 3500, 10)]
CodePudding user response:
You can do what you want, but it's a pretty bad idea.
First, lets explain how to do it. You'll want to use the globals()
builtin, which returns a dictionary to you holding all the global variables defined in the current module. If you edit the dictionary, the global variables will be changed. In your case, you want to add the Stock
object as a value keyed by the ticker
name. That would look like:
def buyStock(self, ticker, price, amount):
#Add stock to portfolio
self.stockPortfolio.append(ticker)
globals()[ticker] = Stock(ticker, price, amount) # make a global variable
Now that you know how to do it, here's why you probably shouldn't. The problem with using the global namespace like this is that you can't (easily) write code that expects a given stock ticker variable to exist. A line like your print(AMZN.amount)
will fail if you bought a different stock instead. Similarly, if you buy another batch of Amazon shares, you'll overwrite the AMZN
variable with a new Stock
object, and will have lost track of how much your first purchase was of, and what price you paid for it.
A much better idea is to put the stock objects into a data structure, such as the list you have in your portfolio already. That way you don't need to use a specific name for it, and you can have more than one stock purchase with the same name, if necessary. I'd use something like this:
def buyStock(self, ticker, price, amount):
#Add stock to portfolio
self.stockPortfolio.append(Stock(ticker, price, amount))
You could print the amount of your latest purchase with print(p.stockPortfolio[-1].amount)
, or write a loop to print out all the stocks (and their amounts or values, maybe) instead of always just getting the first one.