我是靠谱客的博主 激情草莓,最近开发中收集的这篇文章主要介绍Python 可迭代对象、for循环、迭代器与生成器详解导语可迭代对象迭代器生成器总结,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

目录

  • 导语
  • 可迭代对象
    • 一、概念
    • 二、可作用于for循环的可迭代对象
    • 三、自定义可迭代对象实例
    • 四、可迭代对象的for循环机制
  • 迭代器
    • 一、概念
    • 二、内部实现方法
    • 三、与可迭代对象的比较
    • 三、实例
    • 四、迭代器的for循环机制
  • 生成器
    • 一、概念
    • 二、生成器表达式
    • 三、生成器函数
    • 四、yield
  • 总结

导语

在做爬虫的时候用到了生成器的相关知识,当时就觉得生成器太厉害了,超级节省空间,于时便去详细的学习了一下生成器的知识,顺便把迭代器也一起学习了下,网上的讲解挺多的,这里我也总结一下,如有错误,敬请指正。

可迭代对象

一、概念

可迭代对象内部要么实现了__iter__()方法,要么实现了 __getitem__() 方法。

二、可作用于for循环的可迭代对象

  1. 由可迭代对象的概念可以知道,实现了__iter__()方法或__getitem__() 方法便是可迭代对象,然而并不是所有可迭代对象都可直接作用于for循环。(例子后面会讲到)
  2. 可直接作用与for循环的可迭代对象特点:要么实现了能返回迭代器的__iter__()方法,要么实现了 __getitem__() 方法而且其参数是从零开始的索引。

(这个网上有很多不同的见解,这里只说了我的理解,我们通常说的可迭代对象应该是指可作用于for循环的可迭代对象)

三、自定义可迭代对象实例

  1. 实现__iter__()方法:
    1)Iterable(可迭代)类型
    例一:
>>> class B:
...     def __init__(self):
...             pass
...     def __iter__(self):
...             pass
...
>>> z = B()
>>> isinstance(z, Iterable)
True
>>> iter(z)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: iter() returned non-iterator of type 'NoneType'

: : 例二:

>>> class C:
...     def __init__(self):
...             self.List = [1, 3, 5]
...     def __iter__(self):
...             return self
...
>>> I = C()
>>> isinstance(I, Iterable)
True
>>> for i in I:
...     print(i)
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: iter() returned non-iterator of type 'C'
>>> iter(I)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: iter() returned non-iterator of type 'C'

::上面都实现了__iter__()方法,且都被判断为Iterable类型,但是__iter__()方法却没有返回迭代器,于是不能作用于for循环,故而不可迭代。
::2)可作用于for循环的可迭代对象

>>> class Score:
...     def __init__(self):
...             self.score = [1, 3, 4]
...     def __iter__(self):
...             return iter(self.score)
...
>>> s = Score()
>>> isinstance(s, Iterable)
True
>>> isinstance(s, Iterator)
False
>>> for i in s:
...     print(i)
...
1
3
4
>>> iter(s)
<list_iterator object at 0x000001585F4B09E8>
>>>

::上面的__iter__()方法内部有个python内置函数iter(),它的作用是把一个可迭代对象转化为一个迭代器,故__iter__()方法返回的是一个迭代器,因此它是可以作用于for循环的可迭代对象,但却不是一个迭代器,后面我们会讲到。

  1. 实现__getitem__()方法
>>> class words:
...     def __init__(self, word):
...             self.word = word
...     def __getitem__(self, index):
...             return self.word[index]
...
>>> w = words('Hello World')
>>> isinstance(w, Iterable)
False
>>> for i in w:
...     print(i)
...
H
e
l
l
o

W
o
r
l
d
>>> isinstance(w, Iterator)
False
>>> iter(w)
<iterator object at 0x000001585F4B09E8>
>>>

::上面代码没有实现 __iter__() 方法,但是实现了 __getitem__() 方法,而且其参数是从零开始的索引,Python 会创建一个迭代器,尝试按顺序(从索引 0 开始)获取元素。
: :从上面的结果可以看出,实现了 __getitem__() 方法, 它并不是一个Iterable类型,但是它却是一个可作用于for循环的可迭代对象,且能够用iter()方法转化为迭代器。(这里可能有些矛盾,Iterable不就是可迭代对象的意思吗?那为什么它不是Iterable类型呢?我的理解是,从概念可以看出,可迭代对象既包括了Iterable类型,又包括实现了__getitem__() 方法的类。)

四、可迭代对象的for循环机制

从上面的分析我们可以大致了解了在可迭代对象下的for循环机制:

  1. 先判断对象是否为可迭代对象(等价于判断有没有 __iter__() 或 __getitem__() 方法),没有的话直接报错,抛出TypeError异常。
  2. 这里for循环兼容两种机制:有 __iter__() 的话,调用 __iter__() 方法,返回一个迭代器。当for发现如果对象没有 __iter__() ,但是实现了 __getitem__(),会改用下标迭代的方式。
    (iter()方法也会处理这种情况,在不存在 __iter__()的时候,返回一个下标迭代的iterator对象来代替。这也就解释了上面的例子:实现了 __getitem__()方法,不是一个Iterable类型,却可以调用iter()方法转化为一个Iterator类型)
  3. 在python内部不断地调用迭代器的__next__()方法,每次按序返回迭代器中的一个值。
  4. 迭代到最后没有元素时,就抛出异常 StopIteration,for循环会自动处理这个异常终止循环。

迭代器

一、概念

  1. 迭代也便是循环,是访问集合元素的一种方式
  2. 迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。

二、内部实现方法

::迭代器实现了两个基本方法:__iter__() 与 __next__() 。
:: __iter__() :返回一个带有 __next__()方法的对象,也即迭代器本身。
:: __next__() :该方法将逐一访问容器中的元素。 当元素用尽时, __next__()将引发 StopIteration 异常,可以使用 内置函数next()来调用 __next__() 方法。

三、与可迭代对象的比较

  1. 可迭代对象有个 __iter__ ()方法,每次都实例化一个新的迭代器。这样就可以保证不同的迭代过程不会互相影响。
  2. 迭代器实现 __iter__() 方法,返回迭代器本身,因此只能遍历一次,此外还要实现 __next__ ()方法,返回下一个元素
  3. 可作用于for循环或者实现__getitem()__()的可迭代对象可通过内置函数iter()转化为迭代器。
  4. 迭代器一定是可迭代对象,因为它实现了__iter__()方法;

三、实例

  1. 自定义迭代器的构造:
>>> class MyIter(object):
...     def __init__(self, myList):
...             self.myList = myList
...             self.index = 0
...     def __iter__(self):
...             return self
...     def __next__(self):
...             if self.index != len(self.myList):
...                     data = self.myList[self.index]
...                     self.index += 1
...                     return data
...             else:
...                     raise StopIteration
...
>>> m = MyIter([1, 3, 5])
>>> isinstance(m, Iterable)
True
>>> isinstance(m, Iterator)
True
>>> for i in m:
...     print(i)
...
1
3
5
  1. 可迭代对象转化为迭代器
>>> test1 = [1, 3, 5]
>>> isinstance(test1, Iterable)
True
>>> isinstance(test1, Iterator)
False
>>> test3 = 'hello'
>>> isinstance(test3, Iterable)
True
>>> isinstance(test3, Iterator)
False
>>> test4 = {7, 8, 9}
>>> isinstance(test4, Iterable)
True
>>> isinstance(test4, Iterator)
False
>>> test5 = {'x': 10, 'y': 11, 'z': 12}
>>> isinstance(test5, Iterable)
True
>>> isinstance(test5, Iterator)
False
>>> test6 = open('E:/test6.txt', 'r')
>>> isinstance(test6, Iterable)
True
>>> isinstance(test6, Iterator)
True
>>> isinstance(iter(test1), Iterator)
True
>>> isinstance(iter(test5), Iterator)
True

::从上面可以看出,基本数据类型:list、tuple、str、set、dict都是可迭代对象,却不是迭代器,但可以通过iter()方法转化为迭代器。
::但不止上面的类型,只要可作用于for循环或者实现__getitem()__()的可迭代对象可通过内置函数iter()转化为迭代器

四、迭代器的for循环机制

  1. 调用 __iter__()方法,返回自身self,也就是返回迭代器
  2. 不断地调用迭代器的next()方法,每次按序返回迭代器中的一个值
  3. 迭代到最后没有元素时,就抛出异常 StopIteration

生成器

一、概念

  1. 生成器(generator )是一个用于创建迭代器的简单而强大的工具,可以这么说,生成器是一种特殊的迭代器,自然也是一种可迭代对象。其内部实现了__iter__()和__next()__()方法
  2. 生成器函数( generator iterator ):它看起来很像普通函数,不同点在于其包含 yield 表达式以便产生一系列值供给 for-循环使用或是通过 next() 函数逐一获取。
  3. 生成器迭代器(generator iterator):generator 函数所创建的对象。
    每个 yield 会临时暂停处理,记住当前位置执行状态(包括局部变量和挂起的 try 语句)。当该 生成器迭代器恢复时,它会从离开位置继续执行(这与每次调用都从新开始的普通函数差别很大)。
  4. 生成器表达式(generator expression ):返回一个迭代器的表达式。 它看起来很像普通表达式后面带有定义了一个循环变量、范围的 for 子句,以及一个可选的 if 子句。

注意我们通常说的生成器是指生成器函数

二、生成器表达式

  1. 通过列表推导式语法,只不过将[]改为(),但这却不是一个元组推导式,元组没有推导式。
  2. 示例:
>>> gen = (x for x in range(4))
>>> gen
<generator object <genexpr> at 0x000001585F458D68>
>>> for i in gen:
...     print(i)
...
0
1
2
3

再看下一个例子,注意与上面的比较:

>>> gen = (x for x in range(4))
>>> gen
<generator object <genexpr> at 0x000001585F458DE0>
>>> tuple(gen)
(0, 1, 2, 3)
>>> for i in gen:
...     print(i)
...
>>>

这里用for循环并没有取到值,因为生成器也是迭代器,故而其值只能使用一次,前面我们用了tuple()函数将生成器转化为了元组,相当于使用了一次生成器,故而第二次取值时,已经没有值了。

3.特点:
生成器表达式能做的事情列表推导式基本都能处理,只不过在需要处理的序列比较大时,列表推导比较费内存,生成器的优势也就体现出来了。

三、生成器函数

  1. 在函数中如果出现了yield关键字,那么该函数就不再是普通函数,而是生成器函数。
  2. 示例:
    利用生成器实现斐波那契数列(除第一个和第二个数外,任何一个数都可以由前两个相加得到):
>>> def fib(max):
...     a, b, n = 0, 1, 0
...     while n < max:
...             yield b
...             a, b = b, a+b
...             n += 1
...
>>> f = fib(4)
>>> f
<generator object fib at 0x000001585F458D68>
>>> for i in f:
...     print(i, end=' ')
...
1 1 2 3

::内部实现:在 for 循环执行时,每次循环都会执行 fib 函数内部的代码,执行到 yield b 时,fib 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。

  1. 特点:
    生成器函数可以生产一个无限的序列,这是列表根本无法处理的:
>>> def even():
...     n = 0
...     while True:
...             yield n
...             n += 2
...
>>> e = even()
>>> e
<generator object even at 0x000001585F458E58>
>>> count = 0
>>> for i in e:
...     if count >4:
...             break
...     print(i)
...     count += 1
...
0
2
4
6
8

这里我们做了循环终止条件,故不会一直输出,使用生成器不会将所有数据取出来存入内存中,而是返回了一个对象,可以通过对象获取数据;用多少取多少,可以节省内存空间。

四、yield

关于yield的详细知识这里就不说了,推荐一篇浅显易懂的博文:python中yield的用法详解——最简单,最清晰的解释:https://blog.csdn.net/mieleizhi0522/article/details/82142856

总结

网上找的一幅图片,总结得很好:
总结

最后

以上就是激情草莓为你收集整理的Python 可迭代对象、for循环、迭代器与生成器详解导语可迭代对象迭代器生成器总结的全部内容,希望文章能够帮你解决Python 可迭代对象、for循环、迭代器与生成器详解导语可迭代对象迭代器生成器总结所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(59)

评论列表共有 0 条评论

立即
投稿
返回
顶部