ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Python生成器深度解析:构建强大的数据处理管道

Python生成器深度解析:构建强大的数据处理管道 前言生成器是其中的一种核心特性, 它允许我们可以在需要请求新元素的时候才再生成这些元素, 而不是在开始的时候就一次性把全部的元素都生成出来, 它在诸如处理那些规模非常庞大的数据集、用来实现那种能够节省内存的算法以及构建起错综复杂的迭代器模式等多种情况下, 都具有着非常广泛的应用, 在本篇文章的这个章节内容里, 我们将分别从理论和实践这两个相辅相成的方面去深入地探索关于生成器的深层用法。生成器的定义和基本操作生成器其实就属于一种特殊类型的迭代器, 而在通常情况下, 人们创建它的办法就是在函数的定义里面去写上那个叫做yield的关键字。只要当这样的函数被调用的时候, 它就会返回出一个生成器对象来, 接着人们就可以利用next()函数或者通过for循环的方式去拿到新的元素了。def simple_generator():yield Pythonyield isyield awesome# 创建生成器gen simple_generator()# 使用next函数获取元素print(next(gen)) # 输出: Pythonprint(next(gen)) # 输出: isprint(next(gen)) # 输出: awesome# 使用for循环获取元素for word in simple_generator():print(word)# 输出:# Python# is# awesome当生成器用完了, 也就是不再产生更多元素的时候, 再次去调用那个叫做next的函数就会引发一个异常。这个异常我们可以把它手动捕获到, 或者由那个循环去自动处理掉。生成器具有惰性求值这一特点, 同时它还拥有内存优势方面的表现。生成器的一个主要优势在于它们具备惰性求值这一特性, 换句话说, 生成器只会在真正需要的时候才会去计算并产生相应的元素, 这种做法使得开发者在处理规模庞大的数据集合时能够大幅度降低内存的使用量, 与传统的数据结构形式比如列表进行对比可以发现, 生成器并不需要在当前的运行内存中去保存全部的各个元素内容而是选择在每一次执行迭代操作的过程当中动态地来进行新元素的计算工作。这个特点在生成器的身上, 体现得十分明显, 当这些场景出现的时候, 它会有显著的优势可以发挥出来, 比方说处理大规模数据流这样的任务, 又或许是实现复杂的算法之类的操作, 还或者是构建动态的数据管道等情形, 在这样的环境下面, 这个优势就会显现出来了。# 无限序列生成器def infinite_sequence():num 0while True:yield numnum 1# 创建生成器seq infinite_sequence()# 输出前10个元素for i in range(10):print(next(seq))# 输出:# 0# 1# 2# 3# 4# 5# 6# 7# 8# 9在这一个例子之内, 展示的就是一个永远不会停歇的生成器。虽说它能够产生出无数多个元素出来, 但是, 因为是存在有惰性求值这种特性的原因, 所以它是一定不会导致任何关于内存。耗尽。生成器表达式生成器表达式是用一种更加简洁的方法来创建生成器的操作, 它的语法和列表推导式是很相似的, 不过它生成的并不是一个完整的列表对象, 而是生成一个生成器对象, 这样在进行处理大规模数据相关任务的时候, 就可以节省非常多的内存空间。# 创建一个生成器表达式gen_expr (x**2 for x in range(1000000))# 输出前10个元素for i in range(10):print(next(gen_expr))# 输出:# 0# 1# 4# 9# 16# 25# 36# 49# 64# 81在这个例子里, 存在一个生成器表达式, 这个生成器表达式能够生成10^6个元素的平方数, 但因为生成器表达式具有惰性求值这一特性, 所以它并不会在内存里面去把所有这些元素都给生成出来并且存储起来。生成器这个东西, 以及协程这个东西。这个生成器本身还可以被当成协程来使用, 因为协程是一种比较特殊类型的函数, 它能够允许在代码执行的具体过程里把运行状态临时挂起, 随后又重新从刚才停止的地方恢复继续执行, 这种机制就可以让程序在仅仅只有一个线程的情况下也能去完成多个任务同时推进的协作式并发效果, 正因为有了这种能力, 所以才使得我们有机会去利用生成器这样的工具来实现那些非常复杂的控制流程, 比如大家在软件开发领域经常会谈到的那个叫作并发编程的技术还有另外一种被称为异步IO的操作模式等等。def coroutine_generator():print(Starting)while True:value (yield)print(fReceived: {value})# 创建生成器gen coroutine_generator()# 启动生成器next(gen) # 输出: Starting# 向生成器发送数据gen.send(Hello) # 输出: Received: Hellogen.send(Python) # 输出: Received: Python# 关闭生成器gen.close()在这个例子这里它展现的是一个协程形式的生成器功能。关于数据发送的问题, 我们能够借助于send这个函数来实现操作, 具体做法是把指定的数据传给该生成器对象去进行处理。而当生成器处于接收状态并成功获取到这部分数据的之后, 它就会执行相应的动作把接收到的内容给打印输出出来让大家看到。结语生成器实际上是一种非常重要的工具, 借助于这样一个工具, 我们能够以更高效而且更简洁的方式去处理那些比较复杂的问题, 一旦掌握了生成器的具体用法, 就会让你在编程之中获得更高的自由度并且拥有更加强大的实力。One More Thing...在标准库中, 存在一个叫做函数的那个东西儿, 它能用来对那个生成器去做个切片这样的动作, 这就好比我们对列表去进行切片操作那样地做, 这种情况在处理那些大规模的数据流的时候是非常有用的。import itertools# 无限序列生成器def infinite_sequence():num 0while True:yield numnum 1# 创建生成器seq infinite_sequence()# 对生成器进行切片操作sliced_seq itertools.islice(seq, 5, 10)# 输出切片后的元素for num in sliced_seq:print(num)# 输出:# 5# 6#7# 8# 9在这个例子里面, 我们对那个无限序列生成器seq进行了切片操作, 这个操作是调用了.函数来完成的, 然后我们就拿到了这个序列的第5个到第10个元素, 这里需要注意的是, 计数的起点是从0开始的, 这样做的好处是, 让我们在处理那些规模最大的数据流的时候, 能够非常灵活地进行操作, 而且整个过程不会消耗掉大量的内存空间。我们希望这篇文章的深度解析能对你产生积极的帮助。要是你还在生成器方面存在疑惑的情况, 或者你想要深入知晓更多相关内容的知识, 那就请在接下来的评论区域里面进行留言并且参与讨论吧。
返回列表