廖雪峰Python3 学习笔记--切片、迭代、列表生成式、生成器、迭代器

1、切片css

一、切片支持list,tuple和string;python

二、切片的基本格式 object[start:end:step],切片结果不包含object[end];算法

三、倒数切片ruby

>>> L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack']
>>> L[-2:] ['Bob', 'Jack'] >>> L[-2:-1] ['Bob']

2、迭代

一、判断一个对象是可迭代对象呢?方法是经过collections模块的Iterable类型判断:app

 
>>> from collections import Iterable >>> isinstance('abc', Iterable) # str是否可迭代 True >>> isinstance([1,2,3], Iterable) # list是否可迭代 True >>> isinstance(123, Iterable) # 整数是否可迭代 False

二、默认状况下,dict迭代的是key。若是要迭代value,能够用for value in d.values(),若是要同时迭代key和value,能够用for k, v in d.items()函数

因为字符串也是可迭代对象,所以,也能够做用于for循环;ui

三、Python内置的enumerate函数能够把一个list变成索引-元素对,这样就能够在for循环中同时迭代索引和元素自己:spa

>>> for i, value in enumerate(['A', 'B', 'C']): ... print(i, value) ... 0 A 1 B 2 C 

上面的for循环里,同时引用了两个变量,在Python里是很常见的,好比下面的代码:code

>>> for x, y in [(1, 1), (2, 4), (3, 9)]: ... print(x, y) ... 1 1 2 4 3 9

3、列表生成式(List Comprehensions)对象

若是要生成[1x1, 2x2, 3x3, ..., 10x10]怎么作?方法一是循环:

>>> L = [] >>> for x in range(1, 11): ... L.append(x * x) ... >>> L [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] 

可是循环太繁琐,而列表生成式则能够用一行语句代替循环生成上面的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判断,这样咱们就能够筛选出仅偶数的平方:

>>> [x * x for x in range(1, 11) if x % 2 == 0] [4, 16, 36, 64, 100] 

还可使用两层循环,能够生成全排列

>>> [m + n for m in 'ABC' for n in 'XYZ'] ['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ'] 

三层和三层以上的循环就不多用到了。

4、生成器

经过列表生成式,咱们能够直接建立一个列表。可是,受到内存限制,列表容量确定是有限的。并且,建立一个包含100万个元素的列表,不只占用很大的存储空间,若是咱们仅仅须要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

因此,若是列表元素能够按照某种算法推算出来,那咱们是否能够在循环的过程当中不断推算出后续的元素呢?这样就没必要建立完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。

要建立一个generator,有不少种方法。第一种方法很简单,只要把一个列表生成式的[]改为(),就建立了一个generator

>>> 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> 

建立Lg的区别仅在于最外层的[]()L是一个list,而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 

generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

固然,上面这种不断调用next(g)实在是太变态了,正确的方法是使用for循环,由于generator也是可迭代对象:

>>> g = (x * x for x in range(10)) >>> for n in g: ... print(n)

建立了一个generator后,基本上永远不会调用next(),而是经过for循环来迭代它,而且不须要关心StopIteration的错误。
next()forStopIteration
定义generator的另外一种方法,若是一个函数定义中包含关键字,那么这个函数就再也不是一个普通函数,而是一个generator:
yield

generator和函数的执行流程不同。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

举个简单的例子,定义一个generator,依次返回数字1,3,5:

def odd(): print('step 1') yield 1 print('step 2') yield(3) print('step 3') yield(5) 

调用该generator时,首先要生成一个generator对象,而后用next()函数不断得到下一个返回值:

>>> 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不是普通函数,而是generator,在执行过程当中,遇到yield就中断,下次又继续执行。执行3次yield后,已经没有yield能够执行了,因此,第4次调用next(o)就报错。

回到fib的例子,咱们在循环过程当中不断调用yield,就会不断中断。固然要给循环设置一个条件来退出循环,否则就会产生一个无限数列出来。

一样的,把函数改为generator后,咱们基本上历来不会用next()来获取下一个返回值,而是直接使用for循环来迭代:

>>> for n in fib(6): ... print(n) ... 1 1 2 3 5 8 

可是用for循环调用generator时,发现拿不到generator的return语句的返回值。若是想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIterationvalue中:



>>> g = fib(6) >>> while True: ... try: ... x = next(g) ... print('g:', 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

小结

凡是可做用于for循环的对象都是Iterable类型;

凡是可做用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;

集合数据类型如listdictstr等是Iterable但不是Iterator,不过能够经过iter()函数得到一个Iterator对象。

Python的for循环本质上就是经过不断调用next()函数实现的

相关文章
相关标签/搜索