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

    def __repr__(self):
        return f"Stock({self.name!r}, {self.shares!r}, {self.price!r})"

    def __eq__(self, other):
        if not isinstance(other, Stock):
            return False
        return (self.name, self.shares, self.price) == (
            other.name,
            other.shares,
            other.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))
