深入理解python迭代器和生成器


深入理解python迭代器和生成器

理解介质

  • range
  • __iter__
  • __next__
  • iter()
  • next()
  • yield
  • getitem()
  • __getitem__

术语

sequence – 序列

一种 iterable,它支持通过 getitem() 特殊方法来使用整数索引进行高效的元素访问,并定义了一个返回序列长度的 len() 方法。内置的序列类型有 list、str、tuple 和 bytes。注意虽然 dict 也支持 getitem() 和 len(),但它被认为属于映射而非序列,因为它查找时使用任意的 immutable 键而非整数。
collections.abc.Sequence 抽象基类定义了一个更丰富的接口,它在 getitem() 和 len() 之外又添加了 count(), index(), contains() 和 reversed()。 实现此扩展接口的类型可以使用 register() 来显式地注册

iterable – 可迭代对象

能够逐一返回其成员项的对象。 可迭代对象的例子包括所有序列类型 (例如 list, str 和 tuple) 以及某些非序列类型例如 dict, 文件对象 以及定义了 iter() 方法或是实现了 序列 语义的 getitem() 方法的任意自定义类对象。
可迭代对象被可用于 for 循环以及许多其他需要一个序列的地方(zip()、map() …)。当一个可迭代对象作为参数传给内置函数 iter() 时,它会返回该对象的迭代器。这种迭代器适用于对值集合的一次性遍历。在使用可迭代对象时,你通常不需要调用 iter() 或者自己处理迭代器对象。for 语句会为你自动处理那些操作,创建一个临时的未命名变量用来在循环期间保存迭代器。参见 iterator、sequence 以及 generator。

iterator – 迭代器

用来表示一连串数据流的对象。重复调用迭代器的 next() 方法(或将其传给内置函数 next())将逐个返回流中的项。当没有数据可用时则将引发 StopIteration 异常。到这时迭代器对象中的数据项已耗尽,继续调用其 next() 方法只会再次引发 StopIteration 异常。迭代器必须具有 iter() 方法用来返回该迭代器对象自身,因此迭代器必定也是可迭代对象,可被用于其他可迭代对象适用的大部分场合。一个显著的例外是那些会多次重复访问迭代项的代码。容器对象(例如 list)在你每次向其传入 iter() 函数或是在 for 循环中使用它时都会产生一个新的迭代器。如果在此情况下你尝试用迭代器则会返回在之前迭代过程中被耗尽的同一迭代器对象,使其看起来就像是一个空容器。

generator – 生成器

返回一个 generator iterator 的函数。它看起来很像普通函数,不同点在于其包含 yield 表达式以便产生一系列值供给 for-循环使用或是通过 next() 函数逐一获取。
通常是指生成器函数,但在某些情况下也可能是指 生成器迭代器。如果需要清楚表达具体含义,请使用全称以避免歧义。

generator iterator – 生成器迭代器

generator 函数所创建的对象。
每个 yield 会临时暂停处理,记住当前位置执行状态(包括局部变量和挂起的 try 语句)。当该 生成器迭代器 恢复时,它会从离开位置继续执行(这与每次调用都从新开始的普通函数差别很大)。

generator expression – 生成器表达式

返回一个迭代器的表达式。 它看起来很像普通表达式后面带有定义了一个循环变量、范围的 for 子句,以及一个可选的 if 子句。 以下复合表达式会为外层函数生成一系列值


实验

可迭代对象

1
2
3
4
5
6
7
8
9
10
11
12
13
# 简单可迭代对象 
class iterable():

def __init__(self, iterations):
self.iterations = iterations

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

a = iterable([1,2,3,4])

for i in a:
print(i)

迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 简单迭代器 __next__ __iter__
class iterator():

def __init__(self, iterations):
self.iterations = iterations
self.index = -1

def __next__(self):
if self.index < len(self.iterations) - 1:
self.index += 1
else:
raise StopIteration('ending')
return self.iterations[self.index]

def __iter__(self):
return self

a,b = iterator([1,2,3,4]), iterator([1,2,3,4])

for i in b:
print(i)

print(next(a), next(a), next(a), next(a))
next(a)

生成器和生成器迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 生成器
def generator(iterations):
index = len(iterations)
while index:
yield iterations[index - 1]
index -= 1

# 生成器迭代器
generator_iterator = generator([1,2,3,4])

print(hasattr(generator_iterator, '__next__'))
print(hasattr(generator_iterator, '__iter__'))
print(hasattr(generator_iterator, '__getitem__'))

for i in generator_iterator:
print(i)
next(generator_iterator)

生成器表达式

1
2
3
4
5
6
# 生成器表达式
generator_expression = (i for i in range(10))

print(hasattr(generator_expression, '__next__'))
print(hasattr(generator_expression, '__iter__'))
print(hasattr(generator_expression, '__getitem__'))

总结

可迭代对象

  • 可迭代对象必须具有 iter() 方法 和 getitem() 方法
  • 可迭代对象本身不支自遍历(即 __next__() 方法)
  • 可复用

迭代器

  • 迭代器必须具有 iter() 方法 和 next() 方法
  • 迭代器本身不支持切片(即 __getitem__() 方法)
  • 不能复用

生成器

  • 返回生成器迭代器(generator iterator)的函数
  • 含有 yield 关键字

生成器迭代器

  • 生成器(generator)函数所创建的对象

参考资料

术语对照表

dtanys使用技巧


dtanys使用场景

如果不熟悉语法可参考 dtanys项目

核心语法:XDict(Parsing object, Analytical syntax).edict()

Parsing objectlist or dict or tuple

Analytical syntaxstr

  • 提取所有键为 k 的值
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    from dtanys import XDict
    from pprint import pprint
    import requests

    url = 'http://c.3g.163.com/nc/video/list/VAP4BFR16/y/0-10.html'

    res = requests.get(url,headers={'User-Agent':"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 9.50"}).json()

    # 打印数据
    pprint(res)

    # 提取所有mp4的url
    print(XDict(res,'/*mp4_url').edict())
  • 提取对象形如 iterable[dict]的数据
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    from dtanys import XDict
    from pprint import pprint
    import requests

    test_object = [
    {'a': 1, 'b' : 2},
    {'a': 3, 'b' : 4},
    {'a': 5, 'b' : 6, 'e' : 0},
    {'a': 7, 'b' : 8},
    {'a': 9, 'b' : 10, 'c': 0},
    ]

    print(XDict(test_object,"//['a','b']").edict())

注://['k'] 需要添加 '' or "" ,且选取的键值在 iterable 都需要存在

  • 提取分形节点一致的数据
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    from dtanys import XDict
    from pprint import pprint
    import requests

    url = 'http://c.3g.163.com/nc/video/list/VAP4BFR16/y/0-10.html'

    res = requests.get(url,headers={'User-Agent':"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 9.50"}).json()

    # 打印数据
    pprint(res)

    # 提取所有alias,tid,tname,topic_icons 可排序
    print(XDict(res,"/VAP4BFR16//videoTopic['tid','alias','tname','topic_icons']").edict())

  • 配合切片一起使用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    from dtanys import XDict
    from pprint import pprint
    import requests

    url = 'http://c.3g.163.com/nc/video/list/VAP4BFR16/y/0-10.html'

    res = requests.get(url,headers={'User-Agent':"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 9.50"}).json()

    # 打印数据
    # pprint(res)

    # 提取所有res[VAP4BFR16]下标为1,3,5,7,9,10的m3u8_url,mp4_url
    print(XDict(res,"/VAP4BFR16[::2]//['m3u8_url','mp4_url']").edict())

注:当使用切片是选取对象必须是 iterable


其他问题请前往:issues

Lupro


lupro 爬虫框架

lupro是一个完全兼容requests的异步爬虫框架

安装 Lupro

使用 PyPi 安装 Lupro

  • pip Find, install and publish Python packages with the Python Package Index
  • pip install lupro

开始使用

  1. 导入 from lupro import lupro

兼容requests

1
from lupro import lupro as requests

这样即可不用修改代码完全替换 requests

原生lupro

1
2
3
from lupro import lupro
r = lupro.get('https://www.python.org')
r.status_code

批量异步任务

1
2
3
4
5
6
7
8
9
10
11
12
13
from lupro import lupro,lupros,generator,Batchsubmission

# 请求列表
url = ['https://www.python.org','https://www.baidu.com']

# 实例化模板
r = lupro('test',lupros.get(''))

# lupro 生成器
lu = generator(r, url)

# 批量任务
Batchsubmission(lu)

特性

  • 完全继承requests
  • 异步特性
  • lupro生成器
  • 自动编码修正
  • 解析器与解析链
  • 选择器与选择链
  • 下载器
  • 请求头生成器
  • 交互式
  • 微服务

api 文档

完善中

Dtanys


Dtanys

dtanys是一个python字典解析器,让人专注于非数据处理的代码构造中,dtanys使用xpath式语法

安装 Dtanys

使用 PyPi 安装 Dtanys

  • pip Find, install and publish Python packages with the Python Package Index

  • pip install dtanys

开始使用

  1. 导入 from dtanys import XDict

使用场景

1
2
3
4
5
6
test = {
'a':"这是一个测试的字典!",
'b':['python','java','C','C++','go'],
'c':[{'normal':1},{'abnormal':0},{'normal':1}],
'd':{'html':{'content':['css','js']}}
}
  1. 使用路径方式快速定位字典值
  • /d/html/content[0] 等价于 test['d']['html']['content'][0]
  1. 使用 , 选择多个列表值
  • /b[0,3] 等价于 test['d']['b'][0]test['d']['b'][3]
  1. 使用 [start:end:step] 选择多个列表值,完全支持切片操作
  • /b[0:2:1] 等价于 test['d'][0:2:1]
  1. 使用 , 选择多个键值
  • /['a','b'] 等价于 test['a']test['b']
  1. 使用 // 选择所有键值
  • /c//normal 等价于 test['c'][0]['normal']test['c'][2]['normal']
  1. 使用 * 进行泛解析
  • /*normal 等价于 test['c'][0]['normal']test['c'][2]['normal']

example.py 模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
## 获取一个网易云歌单json数据
import json
import requests
from dtanys import XDict

url = "http://music.163.com/api/playlist/detail?id=475934383"

headers = {
'User-Agent':"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11",
}

res = requests.get(url=url,headers=headers).json()

## 打印 json
print(json.dumps(res, sort_keys=True, indent=4,ensure_ascii=False))

## 获取 歌单所有歌名
print(XDict(res,'/result/tracks//name').edict())

更多案例请参考example文件

文档

XDict语法

表达式 描述
/ 从根节点选取
// 从匹配选择的当前节点选择字典中的节点,而不考虑它们的位置
[ any ] 当any为带引号的键时,选取当前对象的键值;否则即为切片或索引
[ ,… ] 要选择多个无规律的索引时,即可使用此方法,可重复选择
* 匹配任何元素节点
XX 从当前节点的键值选取键值为”XX”的值
*XX 从当前节点的键值选取所有键值为”XX”的值

ps : 第一次写 github 项目,如有问题或建议请提Issues或Insight