翻译:《实用的Python编程》06_01_Iteration_protocol
目录 | 上一节 (5.2 封装) | 下一节 (6.2 自定义迭代)
6.1 迭代协议
本节将探究迭代的底层过程。
迭代无处不在
许多对象都支持迭代:
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 "<stdin>", 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 "<stdin>", line 1, in <module>
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
>>>
有关上述代码的一个重要发现——通常,如果一段代码和 Python 的其它代码"类似(speaks the common vocabulary of how other parts of Python normally work)",那么该代码被认为是 “Pythonic” 的。同理,对于容器对象,其重要组成部分应该包括:支持迭代、可以进行索引、对所包含的元素进行判断,以及其它操作等等。
目录 | 上一节 (5.2 封装) | 下一节 (6.2 自定义迭代)
注:完整翻译见 https://github.com/codists/practical-python-zh
翻译:《实用的Python编程》06_01_Iteration_protocol的更多相关文章
- 翻译:《实用的Python编程》InstructorNotes
实用的 Python 编程--讲师说明 作者:戴维·比兹利(David Beazley) 概述 对于如何使用我的课程"实用的 Python 编程"进行教学的问题,本文档提供一些通用 ...
- 翻译:《实用的Python编程》README
欢迎光临 大约 25 年前,当我第一次学习 Python 时,发现 Python 竟然可以被高效地应用到各种混乱的工作项目上,我立即被震惊了.15 年前,我自己也将这种乐趣教授给别人.教学的结果就是本 ...
- 翻译:《实用的Python编程》05_02_Classes_encapsulation
目录 | 上一节 (5.1 再谈字典) | 下一节 (6 生成器) 5.2 类和封装 创建类时,通常会尝试将类的内部细节进行封装.本节介绍 Python 编程中有关封装的习惯用法(包括私有变量和私有属 ...
- 翻译:《实用的Python编程》04_02_Inheritance
目录 | 上一节 (4.1 类) | 下一节 (4.3 特殊方法) 4.2 继承 继承(inheritance)是编写可扩展程序程序的常用手段.本节对继承的思想(idea)进行探讨. 简介 继承用于特 ...
- 翻译:《实用的Python编程》01_02_Hello_world
目录 | 上一节 (1.1 Python) | 下一节 (1.3 数字) 1.2 第一个程序 本节讨论有关如何创建一个程序.运行解释器和调试的基础知识. 运行 Python Python 程序始终在解 ...
- 翻译:《实用的Python编程》03_03_Error_checking
目录 | 上一节 (3.2 深入函数) | 下一节 (3.4 模块) 3.3 错误检查 虽然前面已经介绍了异常,但本节补充一些有关错误检查和异常处理的其它细节. 程序是如何运行失败的 Python 不 ...
- 翻译:《实用的Python编程》03_04_Modules
目录 | 上一节 (3.3 错误检查) | 下一节 (3.5 主模块) 3.4 模块 本节介绍模块的概念以及如何使用跨多个文件的函数. 模块和导入 任何一个 Python 源文件都是一个模块. # f ...
- 翻译:《实用的Python编程》03_05_Main_module
目录 | 上一节 (3.4 模块) | 下一节 (3.6 设计讨论) 3.5 主模块 本节介绍主程序(主模块)的概念 主函数 在许多编程语言中,存在一个主函数或者主方法的概念. // c / c++ ...
- 翻译:《实用的Python编程》04_01_Class
目录 | 上一节 (3.6 设计讨论) | 下一节 (4.2 继承) 4.1 类 本节介绍 class 语句以及创建新对象的方式. 面向对象编程(OOP) 面向对象编程是一种将代码组织成对象集合的编程 ...
随机推荐
- 牛客网-Beauty of Trees 【加权并查集】
锟斤拷锟接o拷https://www.nowcoder.com/acm/contest/119/A锟斤拷源锟斤拷牛锟斤拷锟斤拷 锟斤拷目锟斤拷锟斤拷 It锟斤拷s universally acknow ...
- C++ part8
1.volatile关键字 在C++中,对volatile修饰的对象的访问,有编译器优化上的副作用: 不允许被编译器优化,提供特殊地址的稳定访问(只从内存中读取). 有序性,编译器进行优化时,不能把对 ...
- bzoj1013球形空间产生器sphere 高斯消元(有系统差的写法
Description 有一个球形空间产生器能够在n维空间中产生一个坚硬的球体.现在,你被困在了这个n维球体中,你只知道球面上n+1个点的坐标,你需要以最快的速度确定这个n维球体的球心坐标,以便于摧毁 ...
- msfconsole web后门
一.PHP后门 在meterpreter中有一个名为PHP meterpreter的payload,利用这个可以成功反弹.步骤如下: 1. 使用msfvenom 创建一个webshell.php 2. ...
- nmap进阶使用[脚本篇]
nmap 进阶使用 [ 脚本篇 ] 2017-05-18 NMAP 0x01 前言 因为今天的重点并非nmap本身使用,这次主要还是想给大家介绍一些在实战中相对比较实用的nmap脚本,所以关于 ...
- vue 单文件组件最佳实践
vue 单文件组件最佳实践 生命周期 template <template> <section> <h1>vue single file components te ...
- CSS & new class name
CSS & new class name { test: /\.((s*)css|sass)$/, // test: /\.(css|scss|sass)$/, use: ExtractTex ...
- autocode & API
autocode & API https://autocode.com/app/ https://autocode.com/lib/ api-service https://dashboard ...
- 详解稳定币圣杯USDN
稳定币飞速发展,USDN 一骑绝尘,但因合规问题饱受质疑.合规稳定币作为后来者,奋起直追,亦光耀夺目.而更符合区块链精神的稳定币(抵押其他资产生成稳定币),长期以来只有 Maker 的 DAI 能够在 ...
- NGK的发行量是多少?NGK销毁机制是怎么样的?
代币销毁(Coin Burning),是指将代币从流通中永久性去除.换句话说,被销毁的代币相当于被永久性冻结,再也无法流入市场.那为什么要进行代币销毁呢? 销毁加密货币,可以使剩余加密货币的价值升高, ...