迭代
- 我们可以通过
for
循环来遍历list
或tuple
,这种遍历称为迭代(Iteration)
>>> list = ['Google', 'Neuedu', 'Taobao', 'Baidu']
>>> for x in list:
pass
>>> tup = ('Google', 'Neuedu', 'Taobao')
>>> for x in tup:
... pass
>>> for x, y in [(1, 1), (2, 4), (3, 9)]:
... print(x, y)
-
for
循环不仅可以用在list
或tuple
上,只要是可迭代对象
,都可以迭代。
>>> dict = {'a': 1, 'b': 2, 'c': 3}
>>> for key in dict : # 默认情况下迭代的是key
... pass
>>> for value in dict.values() : # 迭代value
... pass
>>> for k, v in dict.items() : # 同时迭代key和value
... pass
>>> for x in 'abcdef':
... pass
- 判断是否为
可迭代对象
,使用isinstance
函数,将对象
与collections
模块的Iterable
类型进行比较来判断
>>> from collections import Iterable
>>> isinstance('abc', Iterable)
True
>>> isinstance([1,2,3], Iterable)
True
>>> isinstance(123, Iterable)
False
- 练习:使用迭代查找一个list中最小和最大值,并返回一个tuple:
def findMinAndMax(L):
return (None, None)
# 测试
if findMinAndMax([]) != (None, None):
print('测试失败!')
elif findMinAndMax([7]) != (7, 7):
print('测试失败!')
elif findMinAndMax([7, 1]) != (1, 7):
print('测试失败!')
elif findMinAndMax([7, 1, 3, 9, 5]) != (1, 9):
print('测试失败!')
else:
print('测试成功!')
迭代器
迭代器
是一个可以记住元素遍历位置的对象。迭代器
有两个基本函数:iter()
创建迭代器,next()
迭代下一个元素。字符串
、列表
、元组
、字典
、集合
都可用于创建迭代器:- 迭代器只能从集合的第一个元素开始访问,直到所有元素被访问完。
>>> list = [1, 2, 3]
>>> it = iter(list) # 创建迭代器对象
>>> print( next(it) ) # 输出迭代器的下一个元素
1
>>> print( next(it) )
2
>>> print( next(it) )
3
>>> print( next(it) ) # 没有元素可迭代时报错
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
- 迭代器对象通常使用
for
语句进行遍历:
>>> list = [1,2,3,4]
>>> it = iter(list) # 创建迭代器对象
>>> for x in it:
... print (x, end=" ")
1 2 3 4
列表生成式
一种用来创建list
的表达式。
使用
range()
函数可以生成有序的数列列表
>>> list(range(1, 11))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
要生成
[1x1, 2x2, 3x3, ..., 10x10]
怎么办,可以使用循环+range
>>> L = []
>>> for x in range(1, 11):
... L.append(x * x)
但这种循环形式太繁琐,可以使用
列表生成式
来生成
>>> [ x * x for x in range(1, 11) ]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
编写
列表生成式
时,把要生成的元素x * x
放到前面,后面跟for
循环,就可以把list
创建出来
-
for
循环后面还可以加上if
判断,对元素进行筛选
>>> [ x * x for x in range(1, 11) if x % 2 == 0 ]
[4, 16, 36, 64, 100]
-
for
循环中同时迭代两个变量来生成list
>>> d = { 'x': 'A', 'y': 'B', 'z': 'C' }
>>> [ k + '=' + v for k, v in d.items() ]
['y=B', 'x=A', 'z=C']
- 使用双层循环,可以生成全排列
>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
- 可以
调用函数
或调用方法
来生成list
# 列出当前目录下的所有文件和目录名
>>> import os # 导入os模块,模块的概念后面讲到
>>> [d for d in os.listdir('.')] # os.listdir可以列出文件和目录
# 把一个list中所有的字符串变成小写
>>> L = [ 'Hello', 'World', 'IBM', 'Apple' ]
>>> [ s.lower() for s in L ]
['hello', 'world', 'ibm', 'apple']
-
练习:添加
if
语句保证列表生成式能正确地执行
L1 = ['Hello', 'World', 18, 'Apple', None]
L2 = ???
# 测试:
print(L2)
if L2 == ['hello', 'world', 'apple']:
print('测试通过!')
else:
print('测试失败!')
生成器
- 通过
列表生成式
可以直接创建列表。但受到内存限制,列表容量是有限的。而且对于包含了100万个元素的列表,如果仅访问前面几个元素,那么后面的空间都被浪费了。- 如果列表元素可以按照某种算法推算出来,这样就不必创建完整的
list
,而在循环过程中不断推算出后续的元素,从而节省大量的空间。- Python中将这种边循环边计算的机制,称为
生成器(generator)
。
创建生成器
- 创建
生成器(generator)
,只要把列表生成式
的[]
改成()
即可。
>>> g = ( x * x for x in range(10) ) # 创建生成器
>>> type(g)
<class 'generator'>
创建
生成器
后,可以调用next()
函数获得生成器
的下一个返回值,因为生成器
中保存的是算法,每次调用next(g)
时就会计算下一个元素的值,直到没有更多元素时,抛出StopIteration
的错误。
>>> next(g)
0
>>> next(g)
1
更通用的方法是使用
for
循环来迭代它,并且不需要关心StopIteration
错误。
>>> g = ( x * x for x in range(10) )
>>> for n in g:
... print(n)
当函数定义中包含
yield
时,函数将变成生成器
def odd():
print('step 1')
yield 1
print('step 2')
yield(3)
print('step 3')
yield(5)
>>> o = odd()
>>> next(o)
step 1
1
>>> next(o)
step 2
3
>>> next(o)
step 3
5
>>> next(o)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
odd
不是普通函数,而是生成器
,在执行过程中,遇到yield
就中断,下次又继续执行。执行3次yield
后,已经没有yield
可以执行了,所以,第4次调用next(o)
时报错。- 一定要给循环设置
退出条件
,否则会产生无限数列。
如果生成列表的算法比较复杂,无法使用
列表生成式
时,可以用函数
来实现。
创建
生成器
的另一种方法,先定义函数,再将print(b)
改为yield b
。
- 以
斐波那契数列(Fibonacci)
为例,
- 定义函数
def fib(max):
n, a, b = 0, 0, 1
while n < max:
print(b)
a, b = b, a + b
n = n + 1
return 'done'
>>> type(fib)
<class 'function'>
- 将
print
替换为yield
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b # 将print改为yield
a, b = b, a + b
n = n + 1
return 'done'
>>> f = fib(6)
>>> type(f)
<class 'generator'>
>>> f = fib(6)
>>> next(f)
1
>>> next(f)
1
>>> next(f)
2
>>> next(f)
3
>>> next(f)
5
>>> next(f)
8
>>> next(f)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration: done
- 使用
for
循环迭代生成器
>>> for x in fib(6):
... print(x)
...
1
1
2
3
5
8
生成器执行过程
生成器
和函数
的执行流程不一样。函数
是顺序执行,遇到return
语句或最后一行语句时返回。生成器
是在每次调用next()
时候执行,遇到yield
语句时返回,当再次调用next()
时,从上次返回的yield
语句处继续执行。
可迭代对象和迭代器
- 可以直接作用于
for
循环的对象称为可迭代对象(Iterable)
- 可以被
next()
函数调用,并返回下一个值的对象称为迭代器(Iterator)
生成器(generator)
是迭代器(Iterator)
list
、dict
、str
等是可迭代对象(Iterable)
但不是迭代器(Iterator)
迭代器(Iterator)
计算是惰性
的,只有需要返回下一个数据时才会计算
-
练习:把每行看做一个
list
,编写生成器
输出下一行杨辉三角形
1
/ \
1 1
/ \ / \
1 2 1
/ \ / \ / \
1 3 3 1
/ \ / \ / \ / \
1 4 6 4 1
/ \ / \ / \ / \ / \
1 5 10 10 5 1
def triangles():
pass
# 期待输出:
# [1]
# [1, 1]
# [1, 2, 1]
# [1, 3, 3, 1]
# [1, 4, 6, 4, 1]
# [1, 5, 10, 10, 5, 1]
# [1, 6, 15, 20, 15, 6, 1]
# [1, 7, 21, 35, 35, 21, 7, 1]
# [1, 8, 28, 56, 70, 56, 28, 8, 1]
# [1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
n = 0
results = []
for t in triangles():
print(t)
results.append(t)
n = n + 1
if n == 10:
break
if results == [
[1],
[1, 1],
[1, 2, 1],
[1, 3, 3, 1],
[1, 4, 6, 4, 1],
[1, 5, 10, 10, 5, 1],
[1, 6, 15, 20, 15, 6, 1],
[1, 7, 21, 35, 35, 21, 7, 1],
[1, 8, 28, 56, 70, 56, 28, 8, 1],
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
]:
print('测试通过!')
else:
print('测试失败!')
- end -