《实用的Python编程》06_01_Iteration_protocol

迭代无处不在
许多对象都支持迭代:

a = ‘hello’
for c in a: # Loop over characters in a

b = { ‘name’: ‘Dave’, ‘password’:‘foo’}
for k in b: # Loop over keys in dictionary

c = [1,2,3,4]
for i in c: # Loop over items in a list/tuple

f = open(‘foo.txt’)
for x in f: # Loop over lines in a file

迭代:协议
考虑以下 for 语句:

for x in obj:
# statements
for 语句的背后发生了什么?

_iter = obj.iter() # Get iterator object
while True:
try:
x = _iter.next() # Get next item
# statements …
except StopIteration: # No more items
break
所有可应用于 for-loop 的对象都实现了上述底层迭代协议。

示例:手动迭代一个列表。

x = [1,2,3]
it = x.iter()
it
<listiterator object at 0x590b0>

it.next()
1

it.next()
2

it.next()
3

it.next()
Traceback (most recent call last):
File “”, line 1, in ? StopIteration

支持迭代
如果想要将迭代添加到自己的对象中,那么了解迭代非常有用。例如:自定义容器。

class Portfolio:
def init(self):
self.holdings = []

def __iter__(self):
    return self.holdings.__iter__()
...

port = Portfolio()
for s in port:

练习
练习 6.1:迭代演示
创建以下列表:

a = [1,9,4,25,16]
请手动迭代该列表:先调用 iter() 方法获取一个迭代器,然后调用 next() 方法获取下一个元素。

i = a.iter()
i
<listiterator object at 0x64c10>

i.next()
1

i.next()
9

i.next()
4

i.next()
25

i.next()
16

i.next()
Traceback (most recent call last):
File “”, line 1, in
StopIteration

内置函数 next() 是调用迭代器的 next() 方法的快捷方式。尝试在一个文件对象上使用 next() 方法:

f = open(‘Data/portfolio.csv’)
f.iter() # Note: This returns the file itself
<_io.TextIOWrapper name=‘Data/portfolio.csv’ mode=‘r’ encoding=‘UTF-8’>

next(f)
‘name,shares,price\n’

next(f)
‘“AA”,100,32.20\n’

next(f)
‘“IBM”,50,91.10\n’

持续调用 next(f),直到文件末尾。观察会发生什么。

练习 6.2:支持迭代
有时候,你可能想要使自己的类对象支持迭代——尤其是你的类对象封装了已有的列表或者其它可迭代对象时。请在新的 portfolio.py 文件中定义如下类:

portfolio.py

class Portfolio:

def __init__(self, holdings):
    self._holdings = holdings

@property
def total_cost(self):
    return sum([s.cost for s in self._holdings])

def tabulate_shares(self):
    from collections import Counter
    total_shares = Counter()
    for s in self._holdings:
        total_shares[s.name] += s.shares
    return total_shares

Portfolio 类封装了一个列表,同时拥有一些方法,如: total_cost property。请修改 report.py 文件中的 read_portfolio() 函数,以便 read_portfolio() 函数能够像下面这样创建 Portfolio 类的实例:

report.py

import fileparse
from stock import Stock
from portfolio import Portfolio

def read_portfolio(filename):
‘’’
Read a stock portfolio file into a list of dictionaries with keys
name, shares, and price.
‘’’
with open(filename) as file:
portdicts = fileparse.parse_csv(file,
select=[‘name’,‘shares’,‘price’],
types=[str,int,float])

portfolio = [ Stock(d['name'], d['shares'], d['price']) for d in portdicts ]
return Portfolio(portfolio)


接着运行 report.py 程序。你会发现程序运行失败,原因很明显,因为 Portfolio 的实例不是可迭代对象。

import report
report.portfolio_report(‘Data/portfolio.csv’, ‘Data/prices.csv’)
… crashes …
可以通过修改 Portfolio 类,使 Portfolio 类支持迭代来解决此问题:

class Portfolio:

def __init__(self, holdings):
    self._holdings = holdings

def __iter__(self):
    return self._holdings.__iter__()

@property
def total_cost(self):
    return sum([s.shares*s.price for s in self._holdings])

def tabulate_shares(self):
    from collections import Counter
    total_shares = Counter()
    for s in self._holdings:
        total_shares[s.name] += s.shares
    return total_shares

修改完成后, report.py 程序应该能够再次正常运行。同时,请修改 pcost.py 程序,以便能够像下面这样使用新的 Portfolio 对象:

pcost.py

import report

def portfolio_cost(filename):
‘’’
Computes the total cost (shares*price) of a portfolio file
‘’’
portfolio = report.read_portfolio(filename)
return portfolio.total_cost

对 pcost.py 程序进行测试并确保其能正常工作:

import pcost
pcost.portfolio_cost(‘Data/portfolio.csv’)
44671.15

练习 6.3:创建一个更合适的容器
通常,我们创建一个容器类时,不仅希望该类能够迭代,同时也希望该类能够具有一些其它用途。请修改 Portfolio 类,使其具有以下这些特殊方法:

class Portfolio:
def init(self, holdings):
self._holdings = holdings

def __iter__(self):
    return self._holdings.__iter__()

def __len__(self):
    return len(self._holdings)

def __getitem__(self, index):
    return self._holdings[index]

def __contains__(self, name):
    return any([s.name == name for s in self._holdings])

@property
def total_cost(self):
    return sum([s.shares*s.price for s in self._holdings])

def tabulate_shares(self):
    from collections import Counter
    total_shares = Counter()
    for s in self._holdings:
        total_shares[s.name] += s.shares
    return total_shares

现在,使用 Portfolio 类进行一些实验:

import report
portfolio = report.read_portfolio(‘Data/portfolio.csv’)
len(portfolio)
7

portfolio[0]
Stock(‘AA’, 100, 32.2)

portfolio[1]
Stock(‘IBM’, 50, 91.1)

portfolio[0:3]
[Stock(‘AA’, 100, 32.2), Stock(‘IBM’, 50, 91.1), Stock(‘CAT’, 150, 83.44)]

‘IBM’ in portfolio
True

‘AAPL’ in portfolio
False

USB Microphone https://www.soft-voice.com/
Wooden Speakers https://www.zeshuiplatform.com/
亚马逊测评 www.yisuping.cn
深圳网站建设www.sz886.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值