类装饰器在Python中练习

时间:2018-01-01 08:06:52

标签: python oop python-decorators

我从David Beazley的视频课程Python Programming Language学习Python。我被困在这个程序中,作者试图通过@typed来装饰Holding类。

不幸的是,虽然我相信我会逐字复制代码,但它似乎并没有按计划运作。

我复制了函数类型中的代码,位于底部的main()中。它就像一个魅力。

有人可以开导我这个吗?非常感谢。

from typing import Union, List, Iterator
from pprint import pprint
import csv

# Changes
# make Typed.__init__(self, name=None)
# create def typed(cls)
# decorate class Holding with @typed
# no need for the argument in String(), Integer(), Float()


class Typed:
    expected_type = object

    def __init__(self, name=None):
        self.name = name

    def __get__(self, instance, cls):
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        if not isinstance(value, self.expected_type):
            raise TypeError(f'Expected {self.expected_type}')
        instance.__dict__[self.name] = value


class Integer(Typed):
    expected_type = int


class Float(Typed):
    expected_type = float


class String(Typed):
    expected_type = str


def typed(cls):
    for key, obj in vars(cls).items():
        if isinstance(obj, Typed):
            obj.name = key
            print('in typed():', obj.name)
        return cls


@typed
class Holding:
    """Use proxy __setattr__() to limit attributes to design"""
    name = String()
    shares = Integer()
    price = Float()

    def __init__(self, name, date, shares, price):
        self.name = name
        self.date = date
        self.shares = shares
        self.price = price

    def __setattr__(self, name, value):
        if name not in {'name', 'date', 'shares', 'price'}:
            raise AttributeError(f'No attribute {name}')
        super().__setattr__(name, value)

    @property
    def cost(self):
        return self.shares * self.price

    def __str__(self) -> str:
        return f'{self.shares} shares of {self.name} at {self.price}'

    def __repr__(self) -> str:
        return f'Holding({self.name}, {self.date}, {self.shares}, {self.price})'


class Portfolio:
    def __init__(self):
        self.holdings: List[Holding] = []

    def __getattr__(self, name): # provide list like behaviour
        return getattr(self.holdings, name)

    @classmethod
    def from_csv(cls, filename: str) -> 'Portfolio':
        self = cls()
        with open(filename, 'r') as f:
            rows = csv.reader(f)
            headers = next(rows)
            for row in rows:
                h: [Holding] = Holding(row[0], row[1], int(row[2]), float(row[3]))
                self.holdings.append(h)
        return self

    def total_cost(self) -> float:
        return sum([h.shares * h.price for h in self.holdings])

    def __len__(self) -> int:
        return len(self.holdings)

    def __getitem__(self, index: Union[int, str]) -> Union[Holding, List[Holding]]:
        if isinstance(index, str):
            return [h for h in self.holdings if h.name == index]
        else:
            return self.holdings[index]

    def __iter__(self) -> Iterator:
        return iter(self.holdings)


def main():
    h = Holding('ABC', '2018-01-09', 500, 3.45)
    print(h.name, h.date, h.shares, h.price, h.cost)
    # print(h.name, h.date, h.shares, h.price, h.cost)
    # portfolio = Portfolio.from_csv('portfolio.csv')
    # pprint(portfolio.holdings)
    # portfolio.append(Holding('MAC', '2011-12-01', 125, 90.0))
    # print(f'{len(portfolio)} items in the portfolio')
    # pprint(portfolio.holdings)
    try:
        h.shares = 100
        h.time = '10:05AM'
    except Exception as e:
        print(e)

    try:
        h.shares = "200"
    except Exception as e:
        print(e)
    print(h.name, h.date, h.shares, h.price, h.cost)



    for key, obj in vars(Holding).items():
        if isinstance(obj, Typed):
            obj.name = key
            print('in main(): ', obj.name)


if __name__ == '__main__':
    main()

这是输出:

你可以看到h实例不对。

预期:ABC 2018-01-09 500 3.45 1725.0

3.45 2018-01-09 3.45 3.45 11.902500000000002
No attribute time
Expected <class 'int'>
100 2018-01-09 100 100 10000
in main():  name
in main():  shares
in main():  price

1 个答案:

答案 0 :(得分:0)

我发现了问题。

def typed(cls):
    for key, obj in cls.__dict__.items():
    # for key, obj in vars(cls).items(): # Altrenative
        if isinstance(obj, Typed):
            obj.attrname = key
    return cls

返回cls的错误缩进 我还将名称更改为attrname,因为我对许多“名称”感到困惑,但这只是为了便于阅读。