Python之迭代、列表生成式、生成器
一、迭代
1.什么是迭代
说明:在Python中,通过for循环来遍历list、tuple、set、dict,这种遍历我们成为迭代(Iteration)。在Python中,迭代是通过 for … in 来完成的,而很多语言比如C或者Java,迭代list是通过下标完成的,比如Java代码:
int n = 0;
for (i = 0; i < list.length; i++) {
n = list[i];
}
可以看出,Python的for循环抽象程度要高于Java的for循环。
因此,迭代操作就是对于一个集合,无论该集合是有序还是无序,我们用 for 循环总是可以依次取出集合的每一个元素。
注意: 集合是指包含一组元素的数据结构,我们已经介绍的包括:
(1)有序集合:list,tuple,str和unicode;
(2)无序集合:set
(3)无序集合并且具有 key-value 对:dict
迭代与按下标访问数组最大的不同是,后者是一种具体的迭代实现方式,而前者只关心迭代结果,根本不关心迭代内部是如何实现的。
2.索引迭代
说明:索引迭代就是通过迭代list或者tuple得到元素对应的下标。
语法:enumerate()
函数
eg:
L = ['wang', 'zhao', 'li']
for index, name in enumerate(L):
print(index, '-', name)
结果:
0 - wang
1 - zhao
2 - li
注意:enumerate()
函数是把['wang', 'zhao', 'li']
变成了[(0, 'wang'), (1, 'zhao'), (2, 'li')]
即把每个元素变成了一个tuple,通过以下代码可以看出:
L = ['wang', 'zhao', 'li']
for t in enumerate(L):
index = t[0]
name = t[1]
print(index, '-', name)
结果:
0 - wang
1 - zhao
2 - li
3.迭代dict的value
说明:用values()
方法,前面PythonDay03中已经提到过。
了解:python3版本以前还有itervalues()
方法可以获取dict的value,现在没有了。
4.同时迭代dict的key和value
语法:items()
方法
eg:
d = {
'wang':100,
'zhao':80,
'li':60
}
for key, value in d.items():
print(key, '-', value)
结果:
wang - 100
zhao - 80
li - 60
二、列表生成式
1.生成列表
eg:要生成列表[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
L = []
for x in range(1, 11):
L.append(x * x)
print(L)
结果:[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
但是,用循环的方式来实现太繁琐,这里介绍新知识,用列表生成式可以用一行语句代替循环生成上面的list:
print([x * x for x in range(1, 11)])
结果:[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
2.复杂表达式
eg:一个复杂的列表生成式生成一个 HTML 表格:
d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59 }
tds = ['<tr><td>%s</td><td>%s</td></tr>' % (name, score) for name, score in d.items()]
print('<table>')
print('<tr><th>Name</th><th>Score</th><tr>')
print('\n'.join(tds))
print('</table>')
结果:
<table>
<tr><th>Name</th><th>Score</th><tr>
<tr><td>Adam</td><td>95</td></tr>
<tr><td>Lisa</td><td>85</td></tr>
<tr><td>Bart</td><td>59</td></tr>
</table>
在HTML中显示效果为:
Name | Score |
Adam | 95 |
Lisa | 85 |
Bart | 59 |
注意:join()方法可以把一个 list 拼接成一个字符串。
eg:
L = ['1', '2' ,'3', '4']
print('|'.join(L)) #注意:list要想能拼成字符串,这里的list的元素必须为字符串
#join()方法是字符串特有的方法,必须是字符串才能用该方法
结果:1|2|3|4
而字符串的split()
方法与join()方法
刚好相反:
s = '1|2|3|4'
print(s.split('|'))
# 结果:
['1', '2', '3', '4']
3.条件过滤
eg:去掉列表[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
中为偶数平方的数
L = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print([x * x for x in L if x % 2 == 1])
结果:[1, 9, 25, 49, 81]
小测试:请编写一个函数,它接受一个 list,然后把list中的所有字符串变成大写后返回,非字符串元素将被忽略。
代码:
L = ['Hi', 'my name is wang', 1001]
def list_str_upper(L):
return [x.upper() for x in L if isinstance(x, str)]
print(list_str_upper(L))
结果:['HI', 'MY NAME IS WANG']
4.多层表达式
说明:for循环可以嵌套,因此,在列表生成式中,也可以用多层 for 循环来生成列表。
eg:利用 3 层for循环的列表生成式,找出对称的 3 位数。例如,121 就是对称数,因为从右到左倒过来还是 121。
print([x * 100 + y * 10 + n for x in range(1, 10) for y in range(10) for n in range(10) if x == n])
结果:
[101, 111, 121, 131, 141, 151, 161, 171, 181, 191, 202, 212, 222, 232, 242, 252, 262, 272, 282, 292, 303, 313, 323, 333, 343, 353, 363, 373, 383, 393, 404, 414, 424, 434, 444, 454, 464, 474, 484, 494, 505, 515, 525, 535, 545, 555, 565, 575, 585, 595, 606, 616, 626, 636, 646, 656, 666, 676, 686, 696, 707, 717, 727, 737, 747, 757, 767, 777, 787, 797, 808, 818, 828, 838, 848, 858, 868, 878, 888, 898, 909, 919, 929, 939, 949, 959, 969, 979, 989, 999]
三、生成器
1.什么是生成器
说明:列表元素可以按照某种算法,在循环的过程中不断推算出后续的元素。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
作用:使用生成器,可以减少存储数据空间的占用,节省内存空间。
2.创建生成器
(1)把一个列表生成式的[]
改成()
,就创建了一个generator。
eg:
L = [x * x for x in range(10)]
print(L)
g = (x * x for x in range(10))
print(g)
结果:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
<generator object <genexpr> at 0x1022ef630>
(2)如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator
eg:斐波拉契数列的推算规则
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b #本来应该是print(b)
a, b = b, a + b
n = n + 1
return 'done
f = fib(6)
print(f)
结果:<generator object fib at 0x104feaaa0>
注意:a, b = b, a + b
等价于:
t = (b, a + b) # t是一个tuple
a = t[0]
b = t[1]
3.遍历生成器
(1)不断用next()
函数,每用一次next()
函数就是得到下一个元素
eg:
g = (x * x for x in range(10))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
结果:
0
1
4
9
16
(2)用for语句迭代
eg:
g = (x * x for x in range(10))
for n in g:
print(n)
结果:
0
1
4
9
16
25
36
49
64
81
注意:最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return
语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()
的时候执行,遇到yield
语句返回,再次执行时从上次返回的yield
语句处继续执行。
小练习:打印出杨辉三角的前十行(用生成器)。
代码:
def triangles():
n = 0
L = [1]
while n < 10:
yield L
L.append(0)
L = [L[i-1] + L[i] for i in range(len(L))]
n += 1
b = triangles()
for x in b:
print(x)
结果:
[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]
小结:
- 凡是可作用于for循环的对象都是Iterable类型;
- 凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
- 集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。