一文搞懂python 中的迭代器和生成器

可迭代对象和迭代器

迭代(iterate)意味着重复,就像 for 循环迭代序列和字典那样,但实际上也可使用 for 循环迭代其他对象:实现了方法 __iter__ 的对象(迭代器协议的基础)。
__iter__方法返回一个迭代器,它是包含方法 __next__ 的对象,调用时可不提供任何参数;
当你调用 __next__ 时,迭代器应返回其下一个值;如果没有可供返回的值,应引发 StopIteration 异常;
也可使用内置函数 next(),此种情况下,next(it) 与 it.__next()__ 等效。

至于为什么不用列表?因为在很多情况下,使用列表都有点太浪费了。例如,如果你有一个可逐个计算值的函数,你可能只想逐个地获取值,而不是使用列表一次性获取。这是因为如果有很多值,列表可能占用太多的内存。
下面来看一个不能使用列表的示例,因为如果使用,这个列表的长度将是无穷大的!

# 这个“列表”为斐波那契数列,表示该数列的迭代器如下:
class Fibs:
    def __init__(self):
        self.a = 0
        self.b = 1
    def __next__(self):
        self.a, self.b = self.b, self.a + self.b
        return self.a  # 前面逻辑自定义,最后返回下一个值即可
    def __iter__(self):
        return self  # 返回迭代器本身(一个包含 __next__ 方法的对象)

fibs = Fibs()
for f in fibs:
	if f > 1000:
		print(f)  # 1597
		break  # 若不中断循环,将一直循环下去

next(fibs)  # 2584
next(fibs)  # 4181

更正规的定义是,实现了方法 __iter__ 的对象是 可迭代的,再实现了方法 __next__ 的对象是 迭代器。

内置函数 iter()

通过对可迭代对象调用内置函数 iter(),可以获得一个迭代器。还可使用它从函数或其他可调用对象创建可迭代对象。
不过,可迭代对象在转化为迭代器后,会丢失⼀些属性(如 __getitem__() ),但同时也会增加⼀些属性(如 __next__() )。
另外,迭代器一般都是⼀次性的,当迭代过⼀轮后,再次迭代将获取不到元素;而可迭代对象可以重复使用。

it = iter([1, 2, 3])  # list 是可迭代对象哦
next(it)  # 1
next(it)  # 2
next(it)  # 3
next(it)  # StopIretation; 普通的可迭代对象是可复用的,而迭代器是一次性的,回不了头的

it = iter("ABCD")  # string 也是可迭代对象
for i in it:
    print(i, end=" ")  # A B C D
for i in it:
    print(i, end=" ")  # ⽆输出

查看对象是否实现了魔法方法 _iter_ 的四种方法:

# ⽅法1:dir()查看__iter__,详情请自己尝试
dir(2) # 没有
dir("abc") # 有 __iter__()
# ⽅法2:isinstance()判断
import collections
isinstance(2, collections.Iterable) # False
isinstance("abc", collections.Iterable) # True
# ⽅法3:hasattr()判断
hasattr(2,"__iter__") # False
hasattr("abc","__iter__") # True
# ⽅法4:⽤iter()查看是否报错
iter(2) # 报错:'int' object is not iterable
iter("abc") # <str_iterator at 0x1e2396d8f28>

从迭代器创建序列

在可以使用序列的情况下,大多也可以使用迭代器或可迭代对象(诸如索引和切片等操作除外)。迭代器因为缺少 __getitem__ ,因此不能使⽤普通的切⽚语法,暂未深究。

# 使用构造函数 list() 显示的将迭代器转换为列表
class TestIterator:
    value = 0

    def __next__(self):
        self.value += 1
        if self.value > 10: raise StopIteration
        return self.value
    def __iter__(self):
        return self
ti = TestIterator()
ti2 = list(ti)  # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for i in ti2:
    print(i, end=" ")  # 1 2 3 4 5 6 7 8 9 10

print('the second:')

生成器

生成器,也被称为简单生成器(simple generator),生成器自动创建了 iter() 和 next() 方法,是一种使用普通函数语法定义的迭代器。与函数的主要的形式差别就在于,它的函数体中有一句 yield 语句。
每次执行到 yield 处时,生成并返回一个值后,函数都将暂时停止执行,等待下一轮迭代调用,如此往复,直到迭代完。数据量大时,生成器能够极大地节省内存空间。下面还是通过斐波纳契数列来看看:

# 斐波纳契数列的生成器实现: 返回数列的前 n 项
def fibs(n):
    a, b = 0, 1
    for _ in range(n):
        yield a  # 返回的是一个生成器
        a, b = b, b+a
f = fibs(5)
print(f)  # <generator object fibs at 0x05BB20B0>
print(list(f))  # [0, 1, 1, 2, 3]; 此处生成器 f 已经被迭代过一次了
for i in f:
    print(i, end=" ")  # ⽆输出; for循环会⾃动捕捉到 StopIteration 异常并停⽌调⽤ next()

print(next(f))  # StopIteration

与 return 的区别:生成器不是像 return 一样返回一个值,而是可以生成多个值,每次返回一个;return 返回的话,这个函数就结束了。

生成器推导(生成式表达式)

将列表生成式的 [] 改成 () 之后,数据结构将从列表变为生成器,而不是元组。如果要包装可迭代对象(可能生成大量的值)时,若使用列表推导将立即实例化一个列表,从而丧失迭代的优势;但如果使用生成器推导的话,每迭代一次就生成一个值,没必要一次性生成全部值,这样就好的多了。而且,可以直接在既有的圆括号内(如在函数调用中)使用生成器推导时,无需再添加一对圆括号。

L = [x*x for x in range(10)]  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
g = (x*x for x in range(10))  # <generator object <genexpr> at 0x052AF8F0>
print(next(g))  # 0
sum(i ** 2 for i in range(10))  # 285

递归式生成器

创建一个将两层嵌套列表展开的函数:

nested = [[1, 2], [3, 4], [5], 6]
def flatten(nested):
    try:
        for sub in nested:
            for ele in sub:
                yield ele
    except TypeError:
        yield sub

f = flatten(nested)
next(f)  # 1
# print(list(f))  # [2, 3, 4, 5, 6]
for i in f3:
    print(i)  # 2 3 4 5 6

创建一个将任意层嵌套列表展开的函数:
对一层列表进行遍历,遍历下层列表的时候,先对一层遍历出来的元素再调用一次 flatten 函数,这时,如果是不可再迭代的对象的话,就会报 TypeError 错误,捕捉到之后,yeild 返回,继续下一个;如果是可迭代的话,就递归下去;

def flatten(nested):
    try:
        for sub in nested:
            for ele in flatten(sub):
                yield ele
    except TypeError:
        yield nested

nested = [[[1], 2], 3, 4, [5, [6, 7]], 8]
print(list(flatten(nested)))

不过,上面要注意的是:前面也提到了,字符串对象也是可迭代的,而且一般我们也不会将它拆开。更重要的是,字符串的第一个元素是一个长度为 1 的字符串,而长度为 1 的字符串的第一个元素是字符串本身。

s = 'ABCD'
s2 = s[0]  # 'A'
s2[0]  # 'A'

这样子会造成无穷递归的。所以还需要检查一下对象是否类似于字符串:

def flatten(nested):
    try:
        if isinstance(nested, str): raise TypeError

        for sub in nested:
            for ele in flatten(sub):
                yield ele
    except TypeError:
        yield nested

nested = [[[1], '23'], 3, '43', [5, [6, '73']], 8]
print(list(flatten(nested)))  # [1, '23', 3, '43', 5, 6, '73', 8]

不过,它有两个 yield 唉,这认哪个来着???pass

def flatten(nested):
	try:
		for sublist in nested:
			for element in flatten(sublist):
				print("element:", element)
				yield element
	except TypeError:
		print("nested :", nested)
		yield nested

print(list(flatten([[1, 2], [3, 4], [5], 6])))

输出:

nested : 1
element: 1   
nested : 2
element: 2
nested : 3
element: 3
nested : 4
element: 4
nested : 5
element: 5
nested : 6
element: 6
[1, 2, 3, 4, 5, 6]

到此这篇关于python 中的迭代器和生成器简单介绍的文章就介绍到这了,更多相关python 迭代器和生成器内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解Python之可迭代对象,迭代器和生成器

    目录 一.概念描述 二.序列的可迭代性 三.经典的迭代器模式 四.生成器也是迭代器 五.实现惰性迭代器 六.使用生成器表达式简化惰性迭代器 总结 一.概念描述 可迭代对象就是可以迭代的对象,我们可以通过内置的iter函数获取其迭代器,可迭代对象内部需要实现__iter__函数来返回其关联的迭代器; 迭代器是负责具体数据的逐个遍历的,其通过实现__next__函数得以逐个的访问关联的数据元素;同时通过实现__iter__来实现对可迭代对象的兼容; 生成器是一种迭代器模式,其实现了数据的惰性生成,即

  • python三大器之迭代器、生成器、装饰器

    目录 迭代器 生成器 装饰器(非常实用!) 迭代器 聊迭代器前我们要先清楚迭代的概念:通常来讲从一个对象中依次取出数据,这个过程叫做遍历,这个手段称为迭代(重复执行某一段代码块,并将每一次迭代得到的结果作为下一次迭代的初始值).可迭代对象(iterable):是指该对象可以被用于for…in…循环,例如:集合,列表,元祖,字典,字符串,迭代器等. 在python中如果一个对象实现了 __iter__方法,我们就称之为可迭代对象,可以查看set\list\tuple…等源码内部均实现了__iter

  • python的迭代器,生成器和装饰器你了解吗

    python 迭代器与生成器,装饰器 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束. 迭代器有两个基本的方法:iter() 和 next(). 字符串,列表或元组对象都可用于创建迭代器: list1=[1,2,3] s=iter(list1) # 创建迭代器对象 print(next(s)) # 输出迭代器的下一个元素 print(next(s)) print(next(s)) 直到计算出最后一个元素,没有更多的元素时,抛出StopIteration的错误 迭代器对象可以使用

  • python基础之迭代器与生成器

    目录 1. 迭代器 1.1 迭代器的使用 1.2 创建类的迭代器 2. 生成器 2.1 生成器的使用 2.2 生成器表达式 总结 1. 迭代器 1.1 迭代器的使用 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退.迭代器有两个基本的方法:iter() 和 next().字符串,列表或元组对象都可用于创建迭代器. iter(iterable):用于返回可迭代对象的一个迭代器. next(iterator): 从迭代器iterator中获取下一条记录.如果无

  • python中的迭代器,生成器与装饰器详解

    目录 迭代器 生成器 装饰器 总结 迭代器 每一个可迭代类内部都要实现__iter__()方法,返回一个迭代类对象,迭代类对象则定义了这个可迭代类如何迭代. for循环调用list本质上是是调用了list的迭代器进行迭代. # 对list进行for循环本质上是调用了list的迭代器 list = [1,2,3,4] # for 循环调用 for elem in list: print(elem) # 迭代器调用 list_iter = list.__iter__() while True: tr

  • 一文搞懂python 中的迭代器和生成器

    可迭代对象和迭代器 迭代(iterate)意味着重复,就像 for 循环迭代序列和字典那样,但实际上也可使用 for 循环迭代其他对象:实现了方法 __iter__ 的对象(迭代器协议的基础).__iter__方法返回一个迭代器,它是包含方法 __next__ 的对象,调用时可不提供任何参数:当你调用 __next__ 时,迭代器应返回其下一个值:如果没有可供返回的值,应引发 StopIteration 异常:也可使用内置函数 next(),此种情况下,next(it) 与 it.__next(

  • 一文搞懂Python中pandas透视表pivot_table功能详解

    目录 一.概述 1.1 什么是透视表? 1.2 为什么要使用pivot_table? 二.如何使用pivot_table 2.1 读取数据 2.2Index 2.3Values 2.4Aggfunc 2.5Columns 一文看懂pandas的透视表pivot_table 一.概述 1.1 什么是透视表? 透视表是一种可以对数据动态排布并且分类汇总的表格格式.或许大多数人都在Excel使用过数据透视表,也体会到它的强大功能,而在pandas中它被称作pivot_table. 1.2 为什么要使用

  • 一文搞懂Python中Pandas数据合并

    目录 1.concat() 主要参数 示例 2.merge() 参数 示例 3.append() 参数 示例 4.join() 示例 数据合并是数据处理过程中的必经环节,pandas作为数据分析的利器,提供了四种常用的数据合并方式,让我们看看如何使用这些方法吧! 1.concat() concat() 可用于两个及多个 DataFrame 间行/列方向进行内联或外联拼接操作,默认对行(沿 y 轴)取并集. 使用方式 pd.concat( objs: Union[Iterable[~FrameOr

  • 一文搞懂Python中pandas透视表pivot_table功能

    目录 一.概述 1.1 什么是透视表? 1.2 为什么要使用pivot_table? 二.如何使用pivot_table 2.1 读取数据 2.2Index 2.3Values 2.4Aggfunc 2.5Columns 一文看懂pandas的透视表pivot_table 一.概述 1.1 什么是透视表? 透视表是一种可以对数据动态排布并且分类汇总的表格格式.或许大多数人都在Excel使用过数据透视表,也体会到它的强大功能,而在pandas中它被称作pivot_table. 1.2 为什么要使用

  • 一文搞懂Python中列表List和元组Tuple的使用

    目录 列表 List 列表是有序的 列表可以包含任意对象 通过索引访问列表元素 列表嵌套 列表可变 元组 Tuple 定义和使用元组 元素对比列表的优点 元组分配.打包和解包 List 与 Tuple 的区别 列表 List 列表是任意对象的集合,在 Python 中通过逗号分隔的对象序列括在方括号 ( [] ) 中 people_list = ['曹操', '曹丕', '甄姫', '蔡文姫'] print(people_list) ['曹操', '曹丕', '甄姫', '蔡文姫'] peopl

  • 一文搞懂Python中的进程,线程和协程

    目录 1.什么是并发编程 2.进程与多进程 3.线程与多线程 4.协程与多协程 5.总结 1.什么是并发编程 并发编程是实现多任务协同处理,改善系统性能的方式.Python中实现并发编程主要依靠 进程(Process):进程是计算机中的程序关于某数据集合的一次运行实例,是操作系统进行资源分配的最小单位 线程(Thread):线程被包含在进程之中,是操作系统进行程序调度执行的最小单位 协程(Coroutine):协程是用户态执行的轻量级编程模型,由单一线程内部发出控制信号进行调度 直接上一张图看看

  • 一文搞懂Python中subprocess模块的使用

    目录 简介 常用方法和接口 subprocess.run()解析 subprocess.Popen()解析 Popen 对象方法 subprocess.run()案例 subprocess.call()案例 subprocess.check_call()案例 subprocess.getstatusoutput()案例 subprocess.getoutput()案例 subprocess.check_output()案例 subprocess.Popen()综合案例 简介 subprocess

  • 一文搞懂Python中is和==的区别

    目录 ==比较操作符和is同一性运算符区别 哪些情况下is和==结果是完全相同的? 为什么256时相同, 而1000时不同? 结论 ==比较操作符和is同一性运算符区别 哪些情况下is和==结果是完全相同的? 结论 在Python中一切都是对象. Python中对象包含的三个基本要素,分别是:id(身份标识).type(数据类型)和value(值).对象之间比较是否相等可以用==,也可以用is. is和==都是对对象进行比较判断作用的,但对对象比较判断的内容并不相同.下面来看看具体区别在哪? i

  • 一文搞懂​​​​​​​python可迭代对象,迭代器,生成器,协程

    目录 设计模式:迭代 python:可迭代对象和迭代器 为什么要有生成器? python的生成器实现 协程 设计模式:迭代 迭代是一种设计模式,解决有序便利序列的问题.通用的可迭代对象需要支持done和next方法. 伪代码如下: while not iterator.done(): item = iterator.next() ..... python:可迭代对象和迭代器 python的可迭代对象需要实现__iter__()方法,返回一个迭代器.for循环和顶级函数iter(obj)调用obj

  • 一文带你搞懂Python中的文件操作

    目录 一.文件的编码 二.文件的读取 2.1 open()打开函数 2.2 mode常用的三种基础访问模式 2.3 读操作相关方法 三.文件的写入 写操作快速入门 四.文件的追加 追加写入操作快速入门 五.文件操作综合案例 一.文件的编码 计算机中有许多可用编码: UTF-8 GBK Big5 等 UTF-8是目前全球通用的编码格式 除非有特殊需求,否则,一律以UTF-8格式进行文件编码即可. 二.文件的读取 2.1 open()打开函数 注意:此时的f是open函数的文件对象,对象是Pytho

随机推荐