Python3学习笔记 | 十六、Python的语句与语法-迭代器和解析(1)

  • 2019 年 10 月 6 日
  • 筆記

一、迭代器

1、初探

之前章节中,我们看到for语句可以Python任何序列类型,包括列表、元祖以及字符串。如下所示:

>>> for i in [1,2,3]:print(i,end=' ')  ...  1 2 3  >>> for i in (1,2,3):print(i,end=' ')  ...  1 2 3  >>> for i in '123':print(i,end=' ')  ...  1 2 3

下面我们继续看文件迭代器、手动迭代(iter, next)、其它内置类型迭代器。

2、文件迭代器

回忆一下之前章节,文件访问方式有如下:

.read(): 一次性读取全部内容。

.readline(): 一次读取一行。

.readlines(): 生成列表,每一行是每个元素。

.next(): 跟readline()差不多,但读取完之后报错。 next()报错,为StopIteration。在Python中任何这类对象都认为是可迭代的。在Python里迭代工具(比如for)会调用next()来获取数据,并以StopIteration来确认何时离开。

for line in open(<filename>):   print(line, end = '')

因为每个元素在最后包涵n(换行符),因此print函数结尾需要取消结尾中的换行。

注: 尽量不要使用readlines()函数,因为这个会一次性的把所有内容读取到内存里(转换为列表),运行速度会比较慢。最好使用readline或者迭代文件方法。

3、手动迭代

为了支持手动迭代代码,Python支持next()函数,它会自动读取next()函数。next(X)等同于X.next()。

f = open(<filename>)   next(f)

这个会从第一行开始读取内容。

从技术角度来讲,迭代协议里,当使用for函数进行迭代时,会传递给iter内置函数,以便可迭代对象中获取迭代器。返回的对象有需要有next()方法。

>>> L = [1,2,3]  >>> i = iter(L)  >>> next(i)  1  >>> next(i)  2  >>> next(i)  3  >>> next(i)  Traceback (most recent call last):   File "<stdin>", line 1, in <module>  StopIteration

对于文件来说,不需要转换成iter类型的这一步:

>>> file = open(r'C:Testtest.txt')  >>> file is iter(file)  True

因为文件对象就是自己的迭代器,但列表,元祖,字符串等就不是了。

>>> str = '123' ; list = [1,2,3] ; tuple = (1,2,3)  >>> str is iter(str)  False  >>> list is iter(list)  False  >>> tuple is iter(tuple)  False

4、其他内置类型迭代器

除了文件以及像列表这样的实际的序列外,其它类型也有其适用的迭代器。例如,遍历字典键的经典方法是明确地获取其键的列表。

>>> dict = {'a': 1 ,'b' : 2 ,'c' : 3}  >>> for key in dict.keys() :  ...     print(key,dict[key])  ...  a 1  b 2  c 3

二、列表解析

1、初探

遍历列表时,使用for循环来修饰它:

>>> list = [1,2,3]  >>> for i in range(len(list)) :  ...     list[i] += 10  ...  >>> list  [11, 12, 13]

这个是有效的,但不是“最佳实践”。我们可以使用产生所需结果列表的一个单个表达式来完成上面循环:

>>> list = [1,2,3]  >>> list = [i + 10 for i in list]  >>> list  [11, 12, 13]

2、列表解析基础知识

从之前的例子开始分析:list1 = [i + 10 for i in list1]

这个先是运算 [i + 10 for i in list1]之后,再把此赋值给list1。我们来看里面是如何运算:

先是对list1进行迭代,每次把单个值符给i,再进行i + 10,成为新列表的单个元素。这个相当于:

tmp = []  for i in list1:  tmp.append(i+10)  list1 = tmp

可以认为是倒过来的for循环语句。

3、在文件上使用列表解析

>>> file = open(r'C:Testtest.txt').readlines()  >>> file  ['DoraEmonn', 'Daxiongn', 'JingXiang']

在这里发现,每行最后都会有n换行符,这个时候我们可以使用列表解析来进行去除换行符的操作。

>>> file = [line.strip() for line in file]  >>> file  ['DoraEmon', 'Daxiong', 'JingXiang']

或更简单

>>> file = [line.strip() for line in open(r'C:Testtest.txt')]

还可以进行更多操作:

>>> file = [line.strip().upper() for line in open(r'C:Testtest.txt')]  >>> file  ['DORAEMON', 'DAXIONG', 'JINGXIANG']

4、扩展的列表解析语法

扩展语法是在循环基础上,添加了条件:

>>> file = [line.strip().upper() for line in open(r'C:Testtest.txt') if line[0] !='J']  >>> file  ['DORAEMON', 'DAXIONG']

在这里,发现添加 if line[0] != J’的时候,不显示以 “ J “ 开头的行。

>>> [x+y for x in [1,2,3] for y in [10,20,30]]  [11, 21, 31, 12, 22, 32, 13, 23, 33]

我们会发现第二个for循环算是嵌套在第一个for循环。

三、其他迭代环境

map也可用在迭代中:

>>> list(map(str.upper, open(r'C:Testtest.txt')))  ['DORAEMONn', 'DAXIONGn', 'JINGXIANG']

map函数是把后面的可迭代的每个值当作前面的参数传入。在Python3开始正式引入map,之前版本,python2.7也可以使用,但map可以直接返回列表,不需要使用list函数进行转换。后续章节中会继续讲解。上面的可以如下解释:

>>> tmp = []  >>> for line in open(r'C:Testtest.txt'):  ... tmp.append(str.upper(line))  ...  >>> tmp  ['DORAEMONn', 'DAXIONGn', 'JINGXIANG']

相应的,也有sorted,会对迭代对象进行排序后生成列表。

>>> sorted(open(r'C:Testtest.txt'))  ['Daxiongn', 'DoraEmonn', 'JingXiang']

enumerate也会对迭代对象进行运算后,生成可迭代对象。

>>> list(enumerate(open(r'C:Testtest.txt')))  [(0, 'DoraEmonn'), (1, 'Daxiongn'), (2, 'JingXiang')]

enumerate就是在原有的顺序中添加序列号。除了这些,还有filter与reduce方法,这些再后续章节讲解用法。

>>> list(filter(bool,open(r'C:Testtest.txt')))  ['DoraEmonn', 'Daxiongn', 'JingXiang']  >>> import operator,functools  >>> functools.reduce(operator.add,open(r'C:Testtest.txt'))  'DoraEmonnDaxiongnJingXiang'

sum、any、all、max、min也可使用迭代器。

any() 函数用于判断给定的可迭代参数 iterable 是否全部为 False,则返回 False,如果有一个为 True,则返回 True。元素除了是 0、空、FALSE 外都算 TRUE。

all() 函数用于判断给定的可迭代参数 iterable 中的所有元素是否都为 TRUE,如果是返回 True,否则返回 False。元素除了是 0、空、None、False 外都算 True。

>>> sum((1,2,3)),sum([4,5,6])  (6, 15)  >>> any([1,[],'True']), all([1,[],'True'])  (True, False)  >>> max([2,3,1]),min([2,3,1])  (3, 1)

list, tuple, join 都是可以对可迭代对象进行操作后输出:

>>> list(open(r'C:Testtest.txt')),tuple(open(r'C:Testtest.txt')),'**'.join(open(r'C:Testtest.txt'))  (['DoraEmonn', 'Daxiongn', 'JingXiang'], ('DoraEmonn', 'Daxiongn', 'JingXiang'), 'DoraEmonn**Daxiongn**JingXiang')

四、Python3中新的迭代环境

从Python3.x开始,更注重迭代。在Python2.x里,很多函数生成的是列表方式:

>>> zip('abc','123')  [('a', '1'), ('b', '2'), ('c', '3')]

但在Python3.x开始是变成可迭代的特定对象:

>>> zip('abc','123')  <zip object at 0x102295148>  >>> list(zip('abc','123'))  [('a', '1'), ('b', '2'), ('c', '3')]

Python 3.x的这种方式,会延迟计算,在提取内容的时候计算结果。这样会节省内存空间,不需要提前计算后放进内存里。迭代对象,当迭代完成之后,不能再次读取。

>>> a = list(zip('abc','123'))  >>> for i in a: print(i, end=' ')  ...  ('a', '1') ('b', '2') ('c', '3') >>>  >>> for i in a: print(i, end=' ')  ...  >>>

1、range

range从Python3.x开始变为迭代器。 Python 2.x:

>>> range(5)  [0, 1, 2, 3, 4]

Python 3.x:

>>> range(5)  range(0, 5)

所以,在Python 3.x要是需要生成一个有序列表,需要再次转换:

>>> list(range(5))  [0, 1, 2, 3, 4]

但也可以像列表一样使用:

>>> range(5)[1]  1  >>> range(5)[-1]  4

注:range可继续使用。

>>> r = range(7)  >>> for i in r :  ...     print(i,end = ' ')  ...  0 1 2 3 4 5 6 >>>  >>> for i in r :  ...     print(i,end = ' ')  ...  0 1 2 3 4 5 6 >>>

2、map、zip和filter

在Python3.x里,map、zip和filter也是使用迭代器来节约内存开销。但与range不同,使用一次之后,就不能再次使用。

>>> M = map(abs,(-1,3,7))  >>> list(M)  [1, 3, 7]  >>> list(M)  []  >>> Z = zip((1,2,3),('a','b','c'))  >>> list(Z)  [(1, 'a'), (2, 'b'), (3, 'c')]  >>> list(Z)  []  >>> F = filter(bool,(1,'','True'))  >>> list(F)  [1, 'True']  >>> list(F)  []

map() 函数会根据提供的函数对指定序列做映射。第一个参数 function 以参数序列中的每一个元素调用 function 函数,返回包含每次 function 函数返回值的新列表。

zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的对象,这样做的好处是节约了不少的内存。我们可以使用 list() 转换来输出列表。如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同,利用 * 号操作符,可以将元组解压为列表。

filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回一个迭代器对象,如果要转换为列表,可以使用 list() 来转换。该接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判,然后返回 True 或 False,最后将返回 True 的元素放到新列表中。

五、多个迭代器 vs 单个迭代器

之前看到的range,可以同时使用多个迭代器。这个叫range有着多个迭代器。可以进行索引。但其它的,只能迭代一次。

后续章节会看到各种迭代器,而且会说明如何生成两种迭代器。

六、字典视图迭代器

字典视图迭代器,与其它多个迭代器相似(在Python2.x里还是使用列表)

>>> dict = {'a':1,'b':2,'C':3}  >>> dict.keys()  dict_keys(['a', 'b', 'C'])  >>> dict.values()  dict_values([1, 2, 3])  >>> dict.items()  dict_items([('a', 1), ('b', 2), ('C', 3)])

在这里,迭代器都算是多个迭代器(Python3.4为基准)