博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
第八章 Python可迭代对象、迭代器和生成器
阅读量:6243 次
发布时间:2019-06-22

本文共 6341 字,大约阅读时间需要 21 分钟。

8.1 可迭代对象(Iterable)

大部分对象都是可迭代,只要实现了__iter__方法的对象就是可迭代的。

__iter__方法会返回迭代器(iterator)本身,例如:

1
2
3
>>> lst 
= 
[
1
,
2
,
3
]
>>> lst.__iter__()
<listiterator 
object 
at 
0x7f97c549aa50
>

Python提供一些语句和关键字用于访问可迭代对象的元素,比如for循环、列表解析、逻辑操作符等。

判断一个对象是否是可迭代对象:

1
2
3
4
5
6
7
>>> 
from 
collections 
import 
Iterable  
# 只导入Iterable方法
>>> 
isinstance
(
'abc'
, Iterable)     
True
>>> 
isinstance
(
1
, Iterable)     
False
>>> 
isinstance
([], Iterable)
True

这里的isinstance()函数用于判断对象类型,后面会讲到。

可迭代对象一般都用for循环遍历元素,也就是能用for循环的对象都可称为可迭代对象。

例如,遍历列表:

1
2
3
4
5
6
7
>>> lst 
= 
[
1
2
3
]
>>> 
for 
in 
lst:
...   
print 
i
...
1
2
3

8.2 迭代器(Iterator)

具有next方法的对象都是迭代器。在调用next方法时,迭代器会返回它的下一个值。如果next方法被调用,但迭代器没有值可以返回,就会引发一个StopIteration异常。

使用迭代器的好处:

1)如果使用列表,计算值时会一次获取所有值,那么就会占用更多的内存。而迭代器则是一个接一个计算。

2)使代码更通用、更简单。

8.2.1 迭代器规则

回忆下在Python数据类型章节讲解到字典迭代器方法,来举例说明下迭代器规则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> d 
= 
{
'a'
:
1
'b'
:
2
'c'
:
3
}    
>>> d.iteritems()
<dictionary
-
itemiterator 
object 
at 
0x7f97c3b1bcb0
>
# 判断是否是迭代器
>>> 
from 
collections 
import 
Iterator
>>> 
isinstance
(d, Iterator)
False
>>> 
isinstance
(d.iteritems(), Iterator)
True
# 使用next方法。
>>> iter_items 
= 
d.iteritems()
>>> iter_items.
next
()
(
'a'
1
)
>>> iter_items.
next
()
(
'c'
3
)
>>> iter_items.
next
()
(
'b'
2
)

由于字典是无序的,所以显示的是无序的,实际是按照顺序获取的下一个元素。

8.2.2 iter()函数

使用iter()函数转换成迭代器:

语法:

iter(collection) -> iterator

iter(callable, sentinel) -> iterator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> lst 
= 
[
1
2
3
]    
>>> 
isinstance
(lst, Iterator)
False
>>> lst.
next
()  
# 不是迭代器是不具备next()属性的
Traceback (most recent call last):
 
File 
"<stdin>"
, line 
1
in 
<module>
AttributeError: 
'list' 
object 
has no attribute 
'next'
>>> iter_lst 
= 
iter
(lst)             
>>> 
isinstance
(iter_lst, Iterator)
True
>>> iter_lst.
next
()
1
>>> iter_lst.
next
()
2
>>> iter_lst.
next
()
3

8.2.3 itertools模块

itertools模块是Python内建模块,提供可操作迭代对象的函数。可以生成迭代器,也可以生成无限的序列迭代器。

有下面几种生成无限序列的方法:

count([n]) --> n, n+1, n+2, ...

cycle(p) --> p0, p1, ... plast, p0, p1, ...

repeat(elem [,n]) --> elem, elem, elem, ... endlessly or up to n times 

也有几个操作迭代器的方法:

islice(seq, [start,] stop [, step]) --> elements from

chain(p, q, ...) --> p0, p1, ... plast, q0, q1, ...

groupby(iterable[, keyfunc]) --> sub-iterators grouped by value of keyfunc(v) 

imap(fun, p, q, ...) --> fun(p0, q0), fun(p1, q1), ...

ifilter(pred, seq) --> elements of seq where pred(elem) is True

1)count生成序列迭代器

1
2
3
4
5
6
7
8
9
10
>>> 
from 
itertools 
import 
*  
# 导入所有方法    
# 用法 count(start=0, step=1) --> count object
>>> counter 
= 
count()    
>>> counter.
next
()
0
>>> counter.
next
()
1
>>> counter.
next
()
2
......

可以使用start参数设置开始值,step设置步长。

2)cycle用可迭代对象生成迭代器

1
2
3
4
5
6
7
8
# 用法 cycle(iterable) --> cycle object
>>> i 
= 
cycle([
'a'
'b'
'c'
])  
>>> i.
next
()
'a'
>>> i.
next
()
'b'
>>> i.
next
()
'c'

3)repeat用对象生成迭代器

1
2
3
4
5
6
7
8
9
# 用法 repeat(object [,times]) -> create an iterator which returns the object,就是任意对象    
>>> i 
= 
repeat(
1
)
>>> i.
next
()
1
>>> i.
next
()
1
>>> i.
next
()
1
......

可使用无限次。

也可以指定次数:

1
2
3
4
5
6
7
8
9
>>> i 
= 
repeat(
1
2
)    
>>> i.
next
()
1
>>> i.
next
()
1
>>> i.
next
()
Traceback (most recent call last):
 
File 
"<stdin>"
, line 
1
in 
<module>
StopIteration

4)islice用可迭代对象并设置结束位置

1
2
3
4
5
6
7
8
9
10
# 用法 islice(iterable, [start,] stop [, step]) --> islice object    
>>> i 
= 
islice([
1
,
2
,
3
],
2
)   
>>> i.
next
()             
1
>>> i.
next
()
2
>>> i.
next
()
Traceback (most recent call last):
 
File 
"<stdin>"
, line 
1
in 
<module>
StopIteration

正常的话也可以获取的3。

5)chain用多个可迭代对象生成迭代器

1
2
3
4
5
6
7
8
# 用法 chain(*iterables) --> chain object    
>>> i 
= 
chain(
'a'
,
'b'
,
'c'
)
>>> i.
next
()
'a'
>>> i.
next
()
'b'
>>> i.
next
()
'c'

6)groupby将可迭代对象中重复的元素挑出来放到一个迭代器中

1
2
3
4
5
6
7
8
9
10
11
# 用法 groupby(iterable[, keyfunc]) -> create an iterator which returns    
>>> 
for 
key,group 
in 
groupby(
'abcddCca'
):
...   
print 
key,
list
(group)               
...
a [
'a'
]
b [
'b'
]
c [
'c'
]
d [
'd'
'd'
]
C [
'C'
]
c [
'c'
]
a [
'a'
]

groupby方法是区分大小写的,如果想把大小写的都放到一个迭代器中,可以定义函数处理下:

1
2
3
4
5
6
7
8
9
>>> 
for 
key,group 
in 
groupby(
'abcddCca'
lambda 
c: c.upper()):    
...   
print 
key, 
list
(group)
...
A [
'a'
]
B [
'b'
]
C [
'c'
]
D [
'd'
'd'
]
C [
'C'
'c'
]
A [
'a'
]

7)imap用函数处理多个可迭代对象

1
2
3
4
5
6
7
8
# 用法 imap(func, *iterables) --> imap object    
>>> a 
= 
imap(
lambda 
x, y: x 
* 
y,[
1
,
2
,
3
],[
4
,
5
,
6
])   
>>> a.
next
()
4
>>> a.
next
()
10
>>> a.
next
()
18

8)ifilter过滤序列

1
2
3
4
5
6
7
# 用法 ifilter(function or None, sequence) --> ifilter object    
>>> i 
= 
ifilter(
lambda 
x: x
%
2
=
=
0
,[
1
,
2
,
3
,
4
,
5
])
>>> 
for 
in 
i:
...   
print 
i
...
2
4

当使用for语句遍历迭代器时,步骤大致这样的,先调用迭代器对象的__iter__方法获取迭代器对象,再调用对象的__next__()方法获取下一个元素。最后引发StopIteration异常结束循环。


博客地址:http://lizhenliang.blog.51cto.com

QQ群:323779636(Shell/Python运维开发群 

8.3 生成器(Generator)

什么是生成器?

1)任何包含yield语句的函数都称为生成器。

2)生成器都是一个迭代器,但迭代器不一定是生成器。

8.3.1 生成器函数

在函数定义中使用yield语句就创建了一个生成器函数,而不是普通的函数。

当调用生成器函数时,每次执行到yield语句,生成器的状态将被冻结起来,并将结果返回__next__调用者。冻结意思是局部的状态都会被保存起来,包括局部变量绑定、指令指针。确保下一次调用时能从上一次的状态继续。

以生成斐波那契数列举例说明yield使用:

斐波那契(Fibonacci)数列是一个简单的递归数列,任意一个数都可以由前两个数相加得到。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/python
# -*- coding: utf-8 -*-
def 
fab(
max
):
    
n, a, b 
= 
0
0
1
    
while 
n < 
max
:
        
print 
b
        
a, b 
= 
b, a 
+ 
b
        
+
= 
1
fab(
5
)
 
# python test.py
1
1
2
3
5

 使用yied语句,只需要把print b改成yield b即可:

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/python
# -*- coding: utf-8 -*-
def 
fab(
max
):
    
n, a, b 
= 
0
0
1
    
while 
n < 
max
:
        
yield 
b
        
# print b
        
a, b 
= 
b, a 
+ 
b
        
+
= 
1
print 
fab(
5
)
# python test.py
<generator 
object 
fab at 
0x7f2369495820
>

可见,调用fab函数不会执行fab函数,而是直接返回了一个生成器对象,上面说过生成器就是一个迭代器。那么就可以通过next方法来返回它下一个值。

1
2
3
4
5
6
7
8
9
10
11
12
>>> 
import 
test
>>> f 
= 
test.fab(
5
)   
>>> f.
next
()       
1
>>> f.
next
()                               
1
>>> f.
next
()
2
>>> f.
next
()
3
>>> f.
next
()
5

每次fab函数的next方法,就会执行fab函数,执行到yield b时,fab函数返回一个值,下一次执行next方法时,代码从yield b的吓一跳语句继续执行,直到再遇到yield。

8.3.2 生成器表达式

在第四章 Python运算符和流程控制章节讲过,简化for和if语句,使用小括号()返回一个生成器,中括号[]生成一个列表。

回顾下:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 生成器表达式
>>> result 
= 
(x 
for 
in 
range
(
5
))
>>> result
<generator 
object 
<genexpr> at 
0x030A4FD0
>
>>> 
type
(result)
<
type 
'generator'
>
 
# 列表解析表达式
>>> result 
= 
[ x 
for 
in 
range
(
5
)]
>>> 
type
(result)
<
type 
'list'
>
>>> result
[
0
1
2
3
4
]

第一个就是生成器表达式,返回的是一个生成器,就可以使用next方法,来获取下一个元素:

1
2
3
4
5
6
7
>>> result.
next
()
0
>>> result.
next
()
1
>>> result.
next
()
2
......
本文转自 李振良OK 51CTO博客,原文链接:http://blog.51cto.com/lizhenliang/1862637,如需转载请自行联系原作者
你可能感兴趣的文章
[MySQL 5.7]:binlog --statement
查看>>
MicroProfile变成了Eclipse MicroProfile
查看>>
中国电信10G PON演进研究成果卓著:为现网升级铺平道路 加速千兆时代到来
查看>>
家庭宽带市场竞争分析
查看>>
台媒:手机应用和免费wifi可瞬间泄露隐私
查看>>
QUnit单元测试文档
查看>>
手机网络电话(VOIP)大比拼
查看>>
华天动力OA系统全国渠道布局 20个城市分公司初露端倪
查看>>
我市智慧城市建设迈入快车道
查看>>
FSF 鼓励用户抛弃英特尔
查看>>
编程语言漫谈
查看>>
《Python数据科学实践指南》——0.4节一个简单的例子
查看>>
《树莓派学习指南(基于Linux)》——本章小结
查看>>
中国自主操作系统COS宣传片:很好很强大
查看>>
《SolidWorks 2017中文版机械设计从入门到精通)》——2.2 草图命令
查看>>
Google 开发新的开源系统 Fuchsia
查看>>
社区不是请客吃饭(二)不出国门也能参与OpenStack Summit
查看>>
FreeDOS 诞生二十周年
查看>>
新的 OpenID 基金会的董事会领导
查看>>
第十天:估算活动持续时间,类比估算,参数估算,自下而上估算,三点估算解析表...
查看>>