迭代器与生成器 (05)
这周不断优化和调试 sql, 经过精简和改逻辑, sql 也写了一千多行了, 这是数据处理部分, 然后这部分需要做调度 ETL, 生成宽表. 前台部分的 sql 也有几百行, 终于初步上线了, 剩下一些前台的样式慢慢调整, 感觉是接了一个大项目哇, 全部用sql来完成的, 感觉自己 sql 一下就突飞猛进了, 随心所欲地写, 过程中,不断要处理一些数据, 也是灵活用Python 安排上. Python + SQL 简直无敌, 在数据分析这块, 目前我是这样觉得.
然后, 还是抽空把剩下的一点点迭代器的内容, 赶紧来安排一下.
管道 (Pipeline) 方式处理数据
需求
想以类似 Linux 的方式, 迭代处理数据. 如处理一个很大的文件, 要分批, 不能够一次性读入内存中哦
方案
生成器函数 yield 安排上.
通常场景是多个路径下, 多个压缩文件夹, 下有多个文件, 各种乱七八糟格式的. 为了处理这些文件呢, 可以定义一个有多个执行任务的简单生成器函数的容器.
def gen_open(file_names):
"""open a sequence of filenames one at a time producing a file object
the file is closed immediately when proceeding to the next iteration"""
for file_name in file_names:
if file_name.endswith(‘.gz‘):
f = gzip.open(file_name, ‘rt‘)
elif file_name.endswith(‘.bz2‘):
f = bz2.open(file_name, ‘rt‘)
else:
f = open(file_name, ‘rt‘)
yield f
f.close()
def gen_concatenate(iterators):
"""chain a sequence of iterators together into a sigle sequence"""
for it in iterators:
yield from it
def gen_grep(pattern, lines):
"""look of a regex pattern in a sequence of lines"""
pat = re.compile(pattern)
for line in lines:
if pat.sreach(line):
yield line
虽然我平时不咋用这个, 但我总感觉迭代器这些东西蛮高级的, 比如 yield, 我现在写函数, 就优先会想, 能不能用 yield 来代替 return 等...
平铺 (Flatten) 嵌套序列
需求
要将一个多层嵌套的序列, 展开为一个单层列表. 这个就很常用了, 比如咱熟悉的 神经网络, 输入层就是要先将多维矩阵平铺为一个 1 维向量输入呀.
方案
用 yield from 语句, 写一个递归生成器来轻松实现.
# yield from
from collections import Iterable
def flatten(items, ignore_types=(str, bytes)):
for x in items:
if isinstance(x, Iterable) and not isinstance(x, ignore_types):
yield from flatten(x)
else:
yield x
# test
items = [1, [2,2], [3,[4,[5,6,[5]]]]]
for i in flatten(items):
print(i, end=‘,‘)
1,2,2,3,4,5,6,5,
这种骚操作, 我平时就会接触很多了. isinstance(x, Iterable) 用来检查某个元素是否为可迭代的. 如果是 True 的话, yield from 就会返回所有子例程的值. 即一个没有嵌套的简单序列.
参数 ignore_types 和检测语句 isinstance (x, ignore_types) 用来将字符串和字节排除在外, 防止再展开为单个字符.
words = [‘youge‘, [‘adore‘, ‘you‘], ‘at‘, [‘this‘,[‘time‘]]]
for word in flatten(words):
print(word)
youge
adore
you
at
this
time
语句 yield from 在我们想在生成器中调用其他其他生成器,作为子例程的是否非常有用的. 它可以代替额外的 for 循环.
# if not use yield from
def flatten(items, ignore_types=(str, bytes)):
for x in items:
if isinstance(x, Iterable) and not isinstance(x, ignore_types):
for i in flatten(x):
yield i
else:
yield x
这样就会多写一个 for, 显然不如上者简洁和优雅. 还有就是, yield from 在涉及到, 基于微线程和生成器的并发编程中更有大作为, 后面抽空给补充上来.