python:可迭代对象,迭代器,生成器函数,生成器的解析举例代码说明

Posted 呆呆象呆呆

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python:可迭代对象,迭代器,生成器函数,生成器的解析举例代码说明相关的知识,希望对你有一定的参考价值。

0 总述

主要介绍可迭代对象(iterable)、迭代器(iterator)、生成器(generator)。

迭代python最强大的功能之一,是访问集合元素的一种方法。

迭代的概念

迭代是重复反馈过程的活动,其目的通常是为了接近并到达所需的目标或结果。每一次对过程的重复被称为一次“迭代”,而每一次迭代得到的结果会被用来作为下一次迭代的初始值。

1 可迭代对象(iterable)

1.1 定义介绍

  • python中, 某对象实现__iter__()方法或者实现__getitem__()方法而且其参数从0开始索引,那么该对象就是可迭代对象。定义可迭代对象时必须实现__iter__()方法或者__getitem__()方法。
  • 使用iter()方法将可迭代对象变成迭代器
  • 通俗的说就是可以用for循环的对象都是可迭代对象。例如:list、dict、tuple、set、string。其实都是序列,所以说任何序列都是可迭代的对象, 其原因在于他们至少都会实现__getittem__()方法。(序列都可以通过索引获取元素)
  • 是一种通俗的叫法,并不是指某种具体的数据类型。
  • 迭代器有一种具体的迭代器类型,比如list_iteratorset_iterator。可迭代对象实现了__iter__方法,该方法返回一个迭代器对象。

1.2 对可迭代对象进行迭代操作的具体过程

  • 调用可迭代对象的__iter__()方法,返回一个迭代器对象
  • 调用迭代器的__next__()方法返回每一次迭代的内容,直到迭代完成后,抛出stopiteration异常
  • 可迭代对象之所以能迭代,是因为实现了__iter__()方法。当使用for循环时候,解释器会检查对象是否有__iter__ ()方法,有的话就是调用它来获取一个迭代器。所以没有__iter__ ()方法但实现了__getitem__ (),解释器会创建一个迭代器,尝试从0开始按顺序遍历元素。如果尝试失败,Python便会抛出TypeError错误。

所以说虽然字符串、列表、元组、字典、集合等均不是迭代器,但是他们可以在for中进行循环的原因是他们是可迭代对象,在进行迭代操作中会有__iter__()返回对应的迭代器,本质上就是对调用__iter__()后得到的迭代器通过不断使用next()函数实现的,例如:下面举例说明:

list_data = [1,2,3]
list_iterator = iter(list_data)
str_data = "1,2,3"
str_iterator = iter(str_data)
tuple_data = (1,2,3)
tuple_iterator = iter(tuple_data)
dict_data = {"1":1,"2":2,"3":3}
dict_iterator = iter(dict_data)
print(type(list_iterator))
print(type(str_iterator))
print(type(tuple_iterator))
print(type(dict_iterator))

使用while循环模拟for...in的作用

list1 = [1, 2, 3, 4, 5]
iterator = iter(list1)  # 使用iter()转换为迭代器
iterator = list1.__iter__()  # 使用__iter__()转换为迭代器
while True: 
    try: 
        print(iterator.__next__(), end = " ")  # 使用__next__()函数进行迭代
        # print(next(iterator)) # 使用next()函数进行迭代
    except StopIteration as e: 
        break

1.3 创建一个可迭代对象

class Vector(object):
    def __init__(self,components):
        self.components = list(components)
    def __iter__(self):
        return iter(self.components)

V1 = Vector([1,2,3])
for i in V1:
    print(i)

上面的例子中实现了 __iter__()方法,解释器可以从类对象中重复地取出元素并打印。

1.4 判断是否是可迭代对象

如果要检查某一个对象是否为可迭代对象,其实可以使用isinstance()函数,该函数用于判断对象是否为某一类型。

# Python 3.9之前
from collections import Iterable
print(isinstance([1,2,3],Iterable))
# Python 3.9以后
from collections.abc import Iterable
print(isinstance([1,2,3],Iterable))
# 使用typing 库
from typing import Iterable
print(isinstance([1,2,3], Iterable))

举例说明:

class Vector(object):
    def __init__(self,components):
        self.components = list(components)
    def __iter__(self):
        return iter(self.components)

vector_data = Vector([1,2,3])
vector_iterator = iter(vector_data)

list_data = [1,2,3]
list_iterator = iter(list_data)


from typing import Iterable
from typing import Iterator
print("使用typing库Iterable和Iterator")
print("vector_data    :是否为可迭代对象{},是否为迭代器{}".format(isinstance(vector_data, Iterable),isinstance(vector_data, Iterator)))
print("vector_iterator:是否为可迭代对象{},是否为迭代器{}".format(isinstance(vector_iterator, Iterable),isinstance(vector_iterator, Iterator)))
print("list_data      :是否为可迭代对象{},是否为迭代器{}".format(isinstance(list_data, Iterable),isinstance(list_data, Iterator)))
print("list_iterator  :是否为可迭代对象{},是否为迭代器{}".format(isinstance(list_iterator, Iterable),isinstance(list_iterator, Iterator)))


from collections.abc import Iterable
from collections.abc import Iterator
print("使用collections.abc库Iterable和Iterator")
print("vector_data    :是否为可迭代对象{},是否为迭代器{}".format(isinstance(vector_data, Iterable),isinstance(vector_data, Iterator)))
print("vector_iterator:是否为可迭代对象{},是否为迭代器{}".format(isinstance(vector_iterator, Iterable),isinstance(vector_iterator, Iterator)))
print("list_data      :是否为可迭代对象{},是否为迭代器{}".format(isinstance(list_data, Iterable),isinstance(list_data, Iterator)))
print("list_iterator  :是否为可迭代对象{},是否为迭代器{}".format(isinstance(list_iterator, Iterable),isinstance(list_iterator, Iterator)))

注意点

如果可迭代对象只是实现了__getitem__() 的话,abc.Iterable是不考虑该方法的,这便导致了isinstance()判断不准确。更准确的方法应该是尝试调用iter()函数,如果该对象不可迭代,就会抛出TypeError的错误。

from collections.abc import Iterable
class Vector(object):
    def __init__(self,components):
        self.components = list(components)
    
    def __getitem__(self,index):
        return self.components[index]
V1 = Vector([1,2,3])
print(isinstance(V1,Iterable))
V1 = iter(V1)
print(isinstance(V1,Iterable))

2 迭代器(iterator)

  • 迭代器可以记住遍历对象的位置。其内部有一个状态用于记录迭代所在的位置,以便下次迭代时候能取出正确的元素
  • 迭代器有两个基本方法iter()next()。使用iter()方法将可迭代对象变成迭代器。使用next()方法返回下一个值。定义迭代器,必须实现__iter__()__next__()方法。(Python 2 里是需要类内有next()方法)
  • 从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
  • 字符串列表元组对象都可用于创建迭代器
  • 迭代器一定是可迭代对象,但是可迭代对象不一定是迭代器。例如:字符串、字典、元组、集合等。
  • 迭代器就像一个懒加载的工厂,等到有人需要的时候才给它生成值返回,没调用的时候就处于休眠状态等待下一次调用。

2.1 创建一个迭代器

next()方法调用到末尾的时候会跳出StopIterationfor循环等就是在此时终止循环的

list_data = [1,2,3]
interator_data = iter(list_data)
print(interator_data)
print(type(interator_data))
print(next(interator_data))
print(next(interator_data))
print(next(interator_data))
print(next(interator_data))

2.2 判断是否是迭代器

可以参考1.4

2.3 使用for进行迭代器的使用

list_data = [1,2,3]
interator_data = iter(list_data)
print(interator_data)
for i in interator_data:
    print(i,end=" ")

2.4 使用next()进行迭代器的使用

import sys
interator_data = iter([1,2,3]) 
while True:
    try:
        print (next(interator_data), end=" ")
    except StopIteration:
        sys.exit()

2.5 可迭代对象与迭代器的区别

迭代器一定是可迭代对象,但是可迭代对象不一定是迭代器。例如:字符串字典元组集合等。

迭代器可以使用next()方法,但是可迭代对象不一定可以使用next()方法。

一般可以使用iter()方法将可迭代对象变成迭代器

list_data = [1,2,3]
interator_data = iter(list_data)
print(interator_data)
print(type(interator_data))
print(next(interator_data))
print(type(list_data))
print(next(list_data))

2.5 创建一个迭代器

定义一个类作为一个迭代器使用需要在类中实现两个方法 __iter__()__next__()

类中成员函数__iter__() 在外部对类的示例化变量调用iter()方法的时候会被调用,主要功能是实现返回一个特殊的迭代器对象(一般就是自己),(一般也会再次或者在__init__()中设置迭代器的七点)。

类内成员函数__next__()在外部对类的示例化变量调用next()方法的时候会被调用,会返回迭代器中下一个需要被迭代出的值,一般中间应该有StopIteration异常,对迭代的完成进行标识控制。

创建一个返回数字的迭代器,初始值为 1,逐步递增 1:

class My_interator:
    def __iter__(self):
        self.start_number = 1
        return self
 
    def __next__(self):
        if self.start_number <= 10:
            x = self.start_number
            self.start_number += 1
            return x
        else:
            raise StopIteration
 
interator_class = My_interator() # 实例化对象
interator_data = iter(interator_class) # 对实例化对象使用iter() 返回迭代器
 
for x in interator_data:# 对迭代器进行迭代
      print(x,end = " ")

3 生成器(generator)

3.1 定义介绍

  • Python中,使用了yield的函数被称为生成器函数generator function。调用一个生成器函数,返回的是一个实例化迭代器对象
  • 跟普通函数不同的是,生成器函数是一个返回生成器的函数,只能用于迭代操作,更简单点理解生成器就是一种特殊的迭代器生成器自动实现了“迭代器协议”(即__iter__()__next__()方法),不需要再手动实现两方法。
  • 生成器在迭代的过程中可以改变当前迭代值,而修改普通迭代器的当前迭代值往往会发生异常,影响程序的执行。(因为生成器send()方法)
  • 在调用生成器运行的过程中,每次遇到yield 时函数会暂停并保存当前所有的运行信息,返回yield语句表达式的值, 并在下一次执行next()send()方法时从当前位置继续运行。
  • 列表对内存的开销比较大。如果列表元素可以按照某种算法推算出后面的元素,这样就不必创建完整的列表。在python中我们把一边循环一边计算的机制,即为生成器generator

3.2 创建生成器的例子

def myList(num):      # 定义生成器
    now = 0           # 当前迭代值,初始为0
    while now < num:
        val = (yield now)  # 返回当前迭代值,并接受可能的send发送值;
        now = now + 1 if val is None else val  # val为None,迭代值自增1,否则重新设定当前迭代值为val
 
my_list = myList(5)   # 得到一个生成器对象
print my_list.next()  # 返回当前迭代值
print my_list.next()
my_list.send(3)       # 重新设定当前的迭代值
print my_list.next()

3.3 判断是否是生成器函数或生成器

def fab(max): 
    n, a, b = 0, 0, 1 
    while n < max: 
        yield b      # 使用 yield 
        a, b = b, a + b 
        n = n + 1
from inspect import isgeneratorfunction
print(isgeneratorfunction(fab))
print(isgeneratorfunction(fab(5)))

import types 
print(isinstance(fab, types.GeneratorType) )
print(isinstance(fab(5), types.GeneratorType))

3.5 关于更多举例说明

参考专门讲解生成器运行原理和yield关键字的文章。

python中yield的用法

3.6 生成器表达式(generator expression)

生成器表达式是列表推导式的生成器版本,看起来像列表推导式,但是它返回的是一个生成器对象而不是列表对象

a = (x*x for x in range(10))
print(type(a))
print(next(a))
print(next(a))
print(sum(a)) # 注意因为运行了两次之后0,1已经不在生成器能访问的位置中了, sum求和出来为284而不是285

LAST 参考文献

python中可迭代对象、迭代器、生成器等都有什么区别 - 华为云

Python3.9中,使用from collections import Iterable报错_qq_39321623的博客-CSDN博客

Python3 迭代器与生成器 | 菜鸟教程

python中可迭代对象、迭代器、生成器等都有什么区别 - 华为云

黄哥Python:如何判断一个对象是生成器还是迭代器? - 知乎

理解Python可迭代对象、迭代器、生成器 - stardsd - 博客园

跟你深入剖析可迭代对象和迭代器的区别与联系 - SegmentFault 思否

python中yield的用法_呆呆象呆呆的博客-CSDN博客

理解Python可迭代对象、迭代器、生成器 - stardsd - 博客园

Python可迭代对象,迭代器,生成器的区别_Nxin的小抄本-CSDN博客

可迭代对象、迭代器和生成器 - 华为云

(1 封私信 / 82 条消息) 如何更好地理解Python迭代器和生成器? - 知乎

以上是关于python:可迭代对象,迭代器,生成器函数,生成器的解析举例代码说明的主要内容,如果未能解决你的问题,请参考以下文章

python函数五(迭代器,生成器)

python之路——函数迭代,生成器

python:可迭代对象,迭代器,生成器函数,生成器的解析举例代码说明

python:可迭代对象,迭代器,生成器函数,生成器的解析举例代码说明

python编程系列---可迭代对象,迭代器和生成器详解

流畅的python第十四章可迭代的对象,迭代器和生成器学习记录