Home > Net >  How do you use a method parameter as a name for a new object in Python?
How do you use a method parameter as a name for a new object in Python?

Time:02-22

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.

  • Related