一、概念
容器(container):容器是一种把多个元素组织在一起的数据结构,将大部分数据保存在内存中
可迭代对象(Iterable):通俗的说就是在数据类型对象中,只要包含__iter__()
迭代器(Iterator):通俗来讲任何具有__next__()方法的对象都是迭代器
生成器(generator):使用了 yield 的函数被称为生成器(generator)。是一种特殊的、一种更为高级的、更为优雅的迭代器。返回可以迭代对象的函数
二、容器( Container)
容器中的元素可以逐个地迭代获取,可以用in, not in关键字判断元素是否包含在容器中。通常这类数据结构把大部分的元素存储在内存中(也有一些特例,并不是所有的元素都放在内存,比如迭代器和生成器对象),在Python中,常见的容器对象有:
list, deque, ….
set, frozensets, ….
dict, defaultdict, OrderedDict, Counter, ….
tuple, namedtuple, …
str
容器相对来说很好理解,因为你可以把它当成生活中的箱子、房子、船等等里面可以塞任何东西。从技术角度来说,当通过判断一个对象是否包含某个元素来确定它是否为一个容器
尽管绝大多数容器都提供了某种方式来获取其中的每一个元素,但这并不是容器本身提供的能力,而是可迭代对象赋予了容器这种能力,当然并不是所有的容器都是可迭代的,比如:Bloom filter,虽然Bloom filter可以用来检测某个元素是否包含在容器中,但是并不能从容器中获取其中的每一个值,因为Bloom filter压根就没把元素存储在容器中,而是通过一个散列函数映射成一个值保存在数组中。参考:Python3 迭代器和生成器 - 迷鸟归林 - 博客园
三、迭代器(Iterator)
迭代器是 Python 最强大的功能之一,是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。迭代器有两个基本的方法:iter() 和 next()。
字符串,列表或元组对象都可用于创建迭代器,以下示例:
迭代器调用__next__()方法可以获取下一个值, 实际调用过程如下:
那么如何判断一个对象是否是可迭代呢?可迭代对象可以为任意对象,不一定非得是基本数据结构,只要这个对象可以返回一个iterator, 可以使用 collections 模块的 Iterable 类型判断:
把一个类作为一个迭代器使用需要在类中实现两个方法 __iter__()与__next__() 。如果你已经了解的面向对象编程,就知道类都有一个构造函数,Python的构造函数为__init__(), 它会在对象初始化的时候执行。__iter__() 方法返回一个特殊的迭代器对象, 这个迭代器对象实现了__next__() 方法并通过StopIteration异常标识迭代的完成。
下面我们来创建一个迭代器:
StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况,在 __next__() 方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。
可迭代对象: 本质是提供一个中间人,遍历的时候, 需要数据的时候, 获取这个对象的迭代器, 然后通过迭代器依次获取对象中的数据。具备了一个__iter__ 方法的对象就是一个可迭代对象。通过iter方法获得可迭代对象的迭代器, 然后对迭代器使用next() 方法, 获取下一个数据。
迭代器的作用:
迭代器最核心的功能是通过next()函数的调用来返回下一个数据值。如果每次返回的数据值不是在一个已有的数据集合中读取的,而是通过程序按照一定的规律计算生成的 * 可以节省内存和存储空间。如果我们产生的数据很多, 数据量很大的话,很容易把进程跑死或者把服务器跑崩溃。
四、生成器(Generator)
在Python中,使用了 yield 的函数被称为生成器(generator)。跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。调用一个生成器函数,返回的是一个迭代器对象。
生成器是一种特殊的迭代器, 保存当前运行的状态。在Python中有两种类型的生成器:生成器表达式以及生成器函数。生成器函数就是包含 yield 参数的函数。生成器表达式与列表解析式类似。
先看第一种生成器表达式:
第一种方法很简单,只要把一个列表生成式的 [ ] 改成 ( )
L = [x*2 for x in range(5)]
print(L)
# 输出:[0, 2, 4, 6, 8]
G = (x*2 for x in range(5))
print(G)
# 输出:<generator object <genexpr> at 0x000001CDCF78B8C8>
print("length L:", type(L)) # 输出列表的长度 10
print("length G:", len(G)) # TypeError: object of type 'generator' has no len(),因为生成器不能直接给出长度!!!
# 注意点:二者输出等价,不过 G 是在运行时开辟内存,而 L 是直接开辟内存
第二种生成器函数:
第二种方法使用 yeild 函数 ,在函数( __ next __)中使用 yeild 关键字,属于生成器函数。在一般函数中使用 yield 关键字,可以实现一个最简单的生成器,此时这个函数变成一个生成器函数。yield 与 return 返回相同的值,区别在于return返回后,函数状态终止,而yield会保存当前函数的执行状态,在返回后,函数又回到之前保存的状态继续执行。
声明:1.任意生成器都是迭代器(反过来不成立)2.任意生成器,都是一个可以延迟创建值的工厂
以下实例使用 yield 实现斐波那契数列:
那么生成器函数与一般函数的区别:
1.生成器函数包含一个或者多个yield
2.调用生成器函数时,函数将返回一个对象,延迟暂停向下执行
3.__iter__()和__next__()方法等是自动实现,通过next()方法进行迭代对象
4.一旦函数 使用关键字 yield,函数会暂停,控制权返回调用者,局部变量和它们的状态会被保存,直到下一次调用
5.函数终止的时候,StopIteraion会被自动抛出
总之,生成器是Python中一种非常强大的特性,它让我们能够编写更加简洁的代码,同时也更加节省内存,使用CPU也更加高效。
--大多数人想要改造这个世界,但却罕有人想改造自己--