class Stock: _types = (str, int, float) __slots__ = ["name", "_shares", "_price"] def __init__(self, name, shares, price): self.name = name self.shares = shares self.price = price @classmethod def from_row(cls, row): values = [func(val) for func, val in zip(cls._types, row)] return cls(*values) @property def cost(self): return self.shares * self.price @property def shares(self): return self._shares @shares.setter def shares(self, value): if not isinstance(value, self._types[1]): raise TypeError("Expected int") if value < 0: raise ValueError("Expected positive integer") self._shares = value @property def price(self): return self._price @price.setter def price(self, value): if not isinstance(value, self._types[2]): raise TypeError("Expected float") if value < 0.0: raise ValueError("Expected positive float") self._price = value def sell(self, num): self.shares -= num if self.shares < 0: self.shares = 0 def print_portfolio(portfolio): headers = ("name", "shares", "price") fmtstr = "{0: >10} {1: >10} {2: >10}" print(fmtstr.format(*headers)) print("{0:->10} {1:->10} {2:->10}".format("", "", "")) for stock in portfolio: print(fmtstr.format(stock.name, stock.shares, stock.price))