切片:
在Python中对于具有序列结构的数据来说都可以使用切片操作,比如列表(list)中,我们可以用切片取出其中一部分元素。
需要注意的是序列对象某个索引位置返回的是一个元素,而切片操作返回是和被切片对象相同类型对象的副本
如:
>>> alist = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> alist[0]
0
>>> alist[0:1]
[0]
运行截图如下:
切片的语法表达式:
[start_index : end_index : step]
start_index表示起始索引;
stop_index表示结束索引;
step
表示步长,步长不能为0,且默认值为1;
start_index和stop_index不论是正数还是负数索引还是混用都可以,但是要保证 list[stop_index]元素的位置必须在list[start_index]元素的位置右边,否则取不出元素。
如:
>>> L[1:-2]
[1,2,3,4,5,6,7]
运行截图如下:
切片操作是指按照步长,截取从起始索引到结束索引,但不包含结束索引(也就是结束索引减1)的所有元素。
python3
支持切片操作的数据类型有list
、tuple
、string
、unicode
、range;
切片返回的结果类型与原对象类型一致;
切片不会改变原对象,而是重新生成了一个新的对象。
对元组(tuple)也可以使用切片,切片的结果也是一个元组(tuple)。
如:
>>> (0, 1, 2, 3, 4, 5)[:3]
(0, 1, 2)
对字符串也可以用切片,切片时字符串看成是一个list,每个元素是一个字符。切片结果仍是字符串。
>>> 'ABCDEFG'[:3]
'ABC'
运行截图如下:
迭代:
在Python中,对一个列表(list)或元组(tuple)通过for in循环来遍历它,这种遍历就叫做迭代(Iteration)。
Python中列表(list)、元组(tuple)、字典(Dictionary)、字符串、生成器(generator)都是可迭代对象。
注意只要是可以迭代的对象,无论有无下标,都可以迭代。
如:
>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> for key in d:
... print(key)
...
a
c
b
运行截图如下:
默认情况下,dict迭代的是key。如果要迭代value,可以用for value in d.values()
,如果要同时迭代key和value,可以用for k, v in d.items()
。
字符串的迭代举例:
>>> for ch in 'ABC':
... print(ch)
...
A
B
C
运行截图如下:
Python中提供了一个判断函数来判断一个对象是否是可迭代对象。
即collections模块里的Iterable类,使用该类的isinstance()函数即可判断。
如:
>>> from collections import Iterable
>>> isinstance('abc', Iterable) # str是否可迭代
True
>>> isinstance([1,2,3], Iterable) # list是否可迭代
True
>>> isinstance(123, Iterable) # 整数是否可迭代
False
如果在迭代时我们想同时取得元素的下标怎么办?(类似C/C++for循环中从数组取得某个元素后同时得到其下标)
Python内置的enumerate()
函数可以把一个list变成索引-元素对,这样就可以在for
循环中同时迭代索引和元素本身。
如:
>>> for i, value in enumerate(['A', 'B', 'C']):
... print(i, value)
...
0 A
1 B
2 C
运行截图如下:
列表生成式:
列表生成式即List Comprehensions,是Python内置的用来生成列表(list)的特定语法形式的表达式。
列表生成式的格式:
[exp for iter_var in iterable]
运行过程:
迭代iterable中的每个元素;
每次迭代都先把结果赋值给iter_var,然后通过exp得到一个新的计算值;
最后把所有通过exp得到的计算值以一个新列表的形式返回。
如,要生成一个元素从1到10的列表(list):
>>> list(range(1, 11))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
再比如,生成1到10各自的平方的列表(list):
>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
把要生成的元素x * x
放到前面,后面跟for
循环,就可以把创建出这个list。
运行截图如下:
我们还可以进一步在for循环后面加上if判断,筛选出偶数的平方的元素的列表(list):
>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]
运行截图如下:
生成器:
通过上面列表生成式,我们可以直接创建一个列表。但是列表容量肯定是有限的,而且元素很多时,列表会占用很大的存储空间,且不是所有元素都需要访问,这就造成了浪费。
如果列表元素可以按照某种算法推算出来,在循环的过程中不断推算出后续的元素,这样就不必创建完整的列表(list),从而节省大量的空间。在Python中生成器(generator)就是这样一种一边循环一边计算的机制。
生成器的构造方式:
使用类似列表生成式的方式生成 (2*n + 1 for n in range(3, 11)),即把列表生成式最外层方括号改成圆括号;
使用包含yield的函数来生成。
如:
>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x1022ef630>
注意:
Python 3.3之前的版本中,不允许迭代函数法中包含return语句。
注意此时我们无法打印出g代表的generator的每一个元素。
此时我们可以通过next()
函数获得generator的下一个返回值。
>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
25
>>> next(g)
36
>>> next(g)
49
>>> next(g)
64
>>> next(g)
81
>>> next(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
当计算到最后一个元素,没有更多的元素时,Python抛出StopIteration
的错误。
在实际应用时,我们往往使用for
循环,因为generator也是可迭代对象。
如:
>>> g = (x * x for x in range(10))
>>> for n in g:
... print(n)
...
0
1
4
9
16
25
36
49
64
81
运行截图如下:
当推算的算法比较复杂(即上例的x*x),用类似列表生成式的for
循环无法实现的时候,我们还可以用函数来实现。
如著名的斐波拉契数列(Fibonacci):
除第一个和第二个数外,任意一个数都可由前两个数相加得到。
1, 1, 2, 3, 5, 8, 13, 21, 34, ...
def fib(max):
n, a, b = 0, 0, 1
while n < max:
print(b)
a, b = b, a + b
n = n + 1
return 'done'
该函数输出如下:
>>> fib(6)
1
1
2
3
5
8
'done'
上面的函数只要稍加改造就可以变成generator,即只把print(b)
改为yield b
:
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
如果一个函数定义中包含yield
关键字,那么这个函数就不再是一个普通函数,而是一个generator。generator在执行过程中,遇到yield关键字就会中断执行,下次调用则继续从上次中断的位置(即yield语句的下一条语句)继续执行。
把函数改成generator后,我们基本上从来不会用next()
来获取下一个返回值,而是直接使用for
循环来迭代:
>>> for n in fib(6):
... print(n)
...
1
1
2
3
5
8
运行截图如下:
注意:
用for
循环调用generator时,我们取不到generator的return
语句的返回值。
如果想要拿到返回值,必须捕获StopIteration
错误,返回值包含在StopIteration
的value
中。改写上面的程序:
>>> g = fib(6)
>>> while True:
... try:
... x = next(g)
... print(x)
... except StopIteration as e:
... print('Generator return value:', e.value)
... break
...
g: 1
g: 1
g: 2
g: 3
g: 5
g: 8
Generator return value: done
运行截图如下:
迭代器:
迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能前进不能回退。
同生成器类似,迭代器不要求准备好整个迭代过程中所有的元素。仅仅是在迭代至某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这大大节省了系统存储空间。可以被next()
函数调用并不断返回下一个值的对象称为迭代器(Iterator)
。Iterator对象可以被next()
函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration
错误。
可迭代对象(Iterable
)有以下几种:
一类是集合数据类型,如列表(list)、元组(tuple)、字典(Dictionary)、集合(set)、字符串等;
一类是生成器(generator)
,包括列表生成式改写的生成器和带yield
的生成器函数。
前面已经说过,collections模块里的Iterable类,使用该类的isinstance()函数可判断一个对象是否是可迭代对象(Iterable)。
>>> from collections import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False
我们还可以用isinstance()
函数判断一个对象是否是Iterator。
>>> from collections import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('abc', Iterator)
False
生成器都是Iterator
对象,但list
、dict
、str
虽然是Iterable
,却不是Iterator
。
在Python中我们可以使用iter()函数把list
、dict
、str
等Iterable
变成Iterator。
如:
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True
Iterator对象特性:
Iterator对象可以被next()
函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration
错误。这个对象的数据流可以看成一个有序序列,但我们并不知道这个序列的长度,只能不断通过next()
函数计算出下一个数据值,且只有在需要返回下一个数据时它才会计算。这种特性使得Iterator
甚至可以表示一个无限大的数据流,例如全体自然数。
Python的for in
循环本质上就是通过不断调用next()
函数实现的。
如:
for x in [1, 2, 3, 4, 5]:
pass
实际上完全等价于:
# 首先获得Iterator对象:
it = iter([1, 2, 3, 4, 5])
# 循环:
while True:
try:
# 获得下一个值:
x = next(it)
except StopIteration:
# 遇到StopIteration就退出循环
break
总结:
凡是可作用于for
循环的对象都是Iterable
类型;
凡是可作用于next()
函数的对象都是Iterator
类型;
集合数据类型如list
、dict
、str
等是Iterable
但不是Iterator
,不过可以通过iter()
函数获得一个Iterator
对象。