二十、深入Python迭代器和生成器

@Author: Runsen

学习python的过程中,迭代器与生成器是绕不开的话题, 什么是迭代器和生成器呢?

下面我们来了解一下什么是迭代。但在了解迭代器之前,首先需要知道什么是容器。

容器

正所谓:一切都是对象,对象的抽象就是类,而对象的集合就是容器。

容器,就是有多个对象组成的东西。

比如:列表[0,1,2],元组(1,2,3),字典{’0:'0','1':"1'}集合{1,2,3}都是容器。

所有的容器都是可迭代对象,也就是可以使用for循环遍历元素。

# 1、for 循环迭代字符串, 字符串之间使用空格连接
for char in 'RUNSEN':
    print(char, end=' ')
    
输出如下:
R U N S E N

# 2、for 循环迭代 list 列表,列表元素之间使用空格连接
list1 = [i for i in range(5)]  # 这里使用了列表生成表达式
for num in list1:
    print(num, end=' ')
    
输出如下:
0 1 2 3 4

# 3、for 循环迭代 dict (字典)
dict = {'name': 'Runsen', 'age': '21', 'sex': '男'}
# 迭代 dict 中的 key(默认是迭代key)
for key in dict:
    print(key, end=' ')

for value in dict.values():
    print(value, end=' ')

输出如下:
name age sex
Runsen 21 男

# 4、list 中一个元素中还有多个元素
for x, y in [(1, 'a'), (2, 'b'), (3, 'c')]:
    print(x, y)
    
输出如下: 
1 a
2 b
3 c

可迭代对象

所有的容器都是可迭代对象(iterable),从专业角度来讲,只要:内部含有__iter__方法的对象,就是可迭代对象。

因此,我只要使用print('__iter__' in dir(XX)),就能判断XX是不是可迭代对象。

人们常说:列表、元组、字典、字符串都是可迭代对象。数字、布尔值都是不可迭代的。我一试便知。

list,dict(keys(),values(),items()),tuple,str,set,range, 文件句柄(待定)

print('__iter__' in dir(list))
print('__iter__' in dir(tuple))
print('__iter__' in dir(dict))
print('__iter__' in dir(set))
print('__iter__' in dir(str))
print('__iter__' in dir(int))
print('__iter__' in dir(bool))
print('__iter__' in dir([1,2,3]))


输出如下: 
True
True
True
True
True
False
False
True

除了print('__iter__' in dir(XX))判断是不是可迭代对象。还是一种通过Iterableisinstance方法联合使用,进行判断。

from collections import Iterable

print(isinstance('abc', Iterable))
print(isinstance({1, 2, 3}, Iterable))
print(isinstance(1, Iterable))

输出如下: 
True
True
False

注意:list,string,tuple,dict 都属于可迭代对象,但不是迭代器

迭代器

迭代器(iterator)只是提供了一个 next 的方法。调用这个方法后,你要么得到这个可迭代对象的下一个对象,要么得到一个 StopIteration 的错误。

那么,如何声明一个可迭代对象,可以通过__iter__() 来生成可迭代对象,前提是__iter__()传入的参数是容器。

你看下图iter(111)是不是报错了。

因为111不能遍历,所以iter(111)直接报错。

取值

上面说过:迭代器提供了一个next方法,调用这个方法,得到了容器的下一个对象或者一个stopiteration 的报错,具体代码如下所示。

>>> a = iter("123")
>>> next(a)
'1'
>>> next(a)
'2'
>>> next(a)
'3'
>>> next(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

生成器

那么什么又是生成器,和迭代器又有什么关系?

其实,生成器也是迭代器,但更加优雅。使用生成器,我们可以实现与迭代器相同的功能,但不必在类中编写iter()和next()函数

我觉得生成器就是一个迭代器的例子。,如果说迭代器是人,那么生成器就人中的一类人,比如黄人。

为什么会出来一个生成器,其实很简单声明一个迭代器很简单,但是很容易造成内存不够。生成器不会将集合中所有的元素都加载到内存。

比如下图(i for i in range(1000000000)通过元组方式生成生成器。如果使用迭代器,那么当声明迭代器就运行不了了。

[i for i in range(1000000000] 它本是一个迭代器,但因占用的内存太大了,跑不起来,于是古人引出了生成器的概念,当你用的时候,再加载到内存空间中。

在声明生成器中,还有一种方法通过yield关键字。

yieldreturn的区别在于yield并没有终止函数,而return返回值后不再执行函数内代码。

对于yield,在scrapy用的多,然后我在其他地方没有见到过。

引用yield,带yield的函数就叫做生成器,具体示例如下所示。

def test():
    yield 1
    yield 2
    yield 3
t = test()

print(next(t))#output:1
print(next(t))#output:2
print(next(t))#output:3
print(next(t))#output:Traceback (most recent call last):StopIteration

今天也学到了很多东西呢,明天有什么新知识呢?真期待鸭如果喜欢文章可以关注我哦

本文已收录 GitHub,传送门~ ,里面更有大厂面试完整考点,欢迎 Star。

Tags: