共计 4152 个字符,预计需要花费 11 分钟才能阅读完成。
目录 | [上一节 (6.2 自定义迭代)]() | [ 下一节 (6.4 生成器表达式)]()
6.3 生产者,消费者和管道
生成器在设置各种生产者 / 消费者问题(producer/consumer problems)和数据流管道(pipeline)中十分有用。本节将对此进行探讨。
生产者消费者问题
生成器与各种模式的 生产者消费者 问题密切相关。
# Producer | |
def follow(f): | |
... | |
while True: | |
... | |
yield line # Produces value in `line` below | |
... | |
# Consumer | |
for line in follow(f): # Consumes value from `yield` above | |
... |
yield
语句生成给 for
语句生产的值。
生成器管道
你能够应用生成器的这方面个性来设置过程管道(相似于 Unix 管道(pipe))。
producer → processing → processing → consumer
过程管道包含初始的数据生产者、两头的解决阶段、最初的消费者。
producer → processing → processing → consumer
def producer(): | |
... | |
yield item | |
... |
通常状况下,生产者是一个生成器,只管也能够是其它的序列列表。yield
将数据输出管道。
producer → processing → processing → consumer
def consumer(s): | |
for item in s: | |
... |
消费者是一个 for 循环,获取数据(译注:items)并对数据执行某些操作。
producer → processing → processing → consumer
def processing(s): | |
for item in s: | |
... | |
yield newitem | |
... |
两头的解决阶段同时生产和生产数据。它们可能批改数据流,也可能筛选数据流(抛弃数据)。
producer → processing → processing → consumer
def producer(): | |
... | |
yield item # yields the item that is received by the `processing` | |
... | |
def processing(s): | |
for item in s: # Comes from the `producer` | |
... | |
yield newitem # yields a new item | |
... | |
def consumer(s): | |
for item in s: # Comes from the `processing` | |
... |
设置管道的代码如下:
a = producer() | |
b = processing(a) | |
c = consumer(b) |
你会发现数据逐步地流向不同的函数。
练习
对于本练习,stocksim.py
程序仍须要在后盾运行。并且,你将应用到上一节练习(译注:练习 6.7)编写的 follow()
函数。
练习 6.8:创立一个简略的管道
让咱们来看看管道的思维。请创立上面这个函数:
>>> def filematch(lines, substr): | |
for line in lines: | |
if substr in line: | |
yield line | |
>>> |
filematch()
函数除了不再关上文件,简直与上一节练习的第一个生成器示例完全相同——仅仅对作为参数给出的行序列进行操作。当初,请尝试如下操作:
>>> from follow import follow | |
>>> lines = follow('Data/stocklog.csv') | |
>>> ibm = filematch(lines, 'IBM') | |
>>> for line in ibm: | |
print(line) | |
... wait for output ... |
尽管输入可能须要肯定工夫才会呈现,然而,最初你肯定会看到蕴含 IBM 数据的行。
练习 6.9:创立一个简单的管道
通过执行更多操作来进一步了解管道的思维。
>>> from follow import follow | |
>>> import csv | |
>>> lines = follow('Data/stocklog.csv') | |
>>> rows = csv.reader(lines) | |
>>> for row in rows: | |
print(row) | |
['BA', '98.35', '6/11/2007', '09:41.07', '0.16', '98.25', '98.35', '98.31', '158148'] | |
['AA', '39.63', '6/11/2007', '09:41.07', '-0.03', '39.67', '39.63', '39.31', '270224'] | |
['XOM', '82.45', '6/11/2007', '09:41.07', '-0.23', '82.68', '82.64', '82.41', '748062'] | |
['PG', '62.95', '6/11/2007', '09:41.08', '-0.12', '62.80', '62.97', '62.61', '454327'] | |
... |
这十分乏味。你在这里能够看到,follow()
函数的输入被传递到 csv.reader()
函数,并且,咱们当初失去了一系列拆分的行。
练习 6.10:创立更多管道组件
让咱们把这样的思维扩大到更大的管道中。首先,创立 ticker.py
文件,而后在 ticker.py
文件外面创立一个函数,像下面一样读取 CSV 文件:
# ticker.py | |
from follow import follow | |
import csv | |
def parse_stock_data(lines): | |
rows = csv.reader(lines) | |
return rows | |
if __name__ == '__main__': | |
lines = follow('Data/stocklog.csv') | |
rows = parse_stock_data(lines) | |
for row in rows: | |
print(row) |
接着,创立一个抉择特定列的新函数:
# ticker.py | |
... | |
def select_columns(rows, indices): | |
for row in rows: | |
yield [row[index] for index in indices] | |
... | |
def parse_stock_data(lines): | |
rows = csv.reader(lines) | |
rows = select_columns(rows, [0, 1, 4]) | |
return rows |
再次运行程序,你应该能够看到输入放大如下:
['BA', '98.35', '0.16'] | |
['AA', '39.63', '-0.03'] | |
['XOM', '82.45','-0.23'] | |
['PG', '62.95', '-0.12'] | |
... |
再接着,创立一个生成器函数以转换数据类型并构建字典。示例:
# ticker.py | |
... | |
def convert_types(rows, types): | |
for row in rows: | |
yield [func(val) for func, val in zip(types, row)] | |
def make_dicts(rows, headers): | |
for row in rows: | |
yield dict(zip(headers, row)) | |
... | |
def parse_stock_data(lines): | |
rows = csv.reader(lines) | |
rows = select_columns(rows, [0, 1, 4]) | |
rows = convert_types(rows, [str, float, float]) | |
rows = make_dicts(rows, ['name', 'price', 'change']) | |
return rows | |
... |
再次运行程序,你应该可能看到像上面这样的字典流:
{'name':'BA', 'price':98.35, 'change':0.16} | |
{'name':'AA', 'price':39.63, 'change':-0.03} | |
{'name':'XOM', 'price':82.45, 'change': -0.23} | |
{'name':'PG', 'price':62.95, 'change':-0.12} | |
... |
练习 6.11:筛选数据
创立一个筛选数据的函数。示例:
# ticker.py | |
... | |
def filter_symbols(rows, names): | |
for row in rows: | |
if row['name'] in names: | |
yield row |
应用该函数能够筛选出投资组合中的股票:
import report | |
portfolio = report.read_portfolio('Data/portfolio.csv') | |
rows = parse_stock_data(follow('Data/stocklog.csv')) | |
rows = filter_symbols(rows, portfolio) | |
for row in rows: | |
print(row) |
练习 6.12:整合所有的代码
请在 ticker.py
文件中编写函数 ticker(portfile, logfile, fmt)
,该函数依据给定的投资组合、日志文件和表格格局创立实时的股票报价器。示例:
>>> from ticker import ticker | |
>>> ticker('Data/portfolio.csv', 'Data/stocklog.csv', 'txt') | |
Name Price Change | |
---------- ---------- ---------- | |
GE 37.14 -0.18 | |
MSFT 29.96 -0.09 | |
CAT 78.03 -0.49 | |
AA 39.34 -0.32 | |
... | |
>>> ticker('Data/portfolio.csv', 'Data/stocklog.csv', 'csv') | |
Name,Price,Change | |
IBM,102.79,-0.28 | |
CAT,78.04,-0.48 | |
AA,39.35,-0.31 | |
CAT,78.05,-0.47 | |
... |
探讨
心得体会:你能够创立各种生成器函数,并把它们链接在一起执行波及数据流的管道解决。另外,你能够创立一个函数,把一系列的管道阶段打包到一个独自的函数中调用(例如 parse_stock_data()
函数)。
目录 | [上一节 (6.2 自定义迭代)]() | [ 下一节 (6.4 生成器表达式)]()
注:残缺翻译见 https://github.com/codists/practical-python-zh