Python的生成器函数提供了一种强大的机制来管理数据和计算资源,但是对于Python的新手来说,它们不一定直观。在本文中,我将分解生成器的机制,同时还介绍我希望是一个有启发性的示例:用于管理和流传输S3文件资源的小类。

简介与历史

鉴于使用Python入门和编写实际完成某件事的代码非常容易(例如,遍历值列表,计算和/或打印这些值),对于新手或不经意的Python程序员来说,使用该语言可能并不陌生建立在拖延或延迟计算的概念中。语言本身固有的这种松散(或懒惰)对于那些使用编译语言(例如C ++)的人来说似乎是陌生的。

大多数程序员学习“ 惰性评估 ”,并被教导编写代码以实现这种做法。但是Python在语言中对此的原生支持(通过单个关键字简单而优雅地实现)引入了功能和表达能力,这在其他编程语言中似乎很少见。毫不奇怪的是,作为“ lambda演算 ”的一部分引入了惰性评估作为概念,并且Python(尽管不是唯一的功能语言(例如Lisp))体现了这种功能编程DNA。(我之前写过关于Python函数装饰器的文章,Python对闭包的使用也是lambda演算的遗产的一部分)。

“ PEP 255-Simple Generators ”在2001年引入了generators,将懒惰评估的一种稍微倾斜的表达作为动机:

当生产者职能部门的工作非常艰苦,需要维持所产生的值之间的状态时,大多数编程语言都无法提供令人满意且有效的解决方案……。


机械学

Python生成器函数是一个功能强大的概念,但是与复杂的函数装饰器框架不同,它们是通过非常简单的机制来实现或表示的,即“ yield”语句(yield是PEP 255下添加到Python的新关键字)。

作为及物动词,yield表示生产;作为不及物动词,它表示让步或放弃。单词的两种含义都在Python的生成器函数中起作用。

传统上,我们认为函数在返回单个值,列表或字典形式的多个值或用户定义的对象时通过它们的return语句产生结果。我们认为return语句是函数结束控制并将控制和结果割让回其调用方的一种方式。在return语句之后,运行时环境(解释器)将给定函数的堆栈框架弹出调用堆栈,并且给定函数的“环境”(如其存在)将不复存在(直到下一次调用该函数)。

Python的yield语句完全改变了这种行为。让我们看一下生成器的一个非常简单且人为的示例-带有一些其他代码来演示其用法(代码来自iPython解释器交互式会话):

In [8]: def gen(x): ...: yield x In [9]: g = gen(10)In [10]: gOut[10]: In [11]: next(g)Out[11]: 10In [12]: g = gen(10)In [13]: gOut[13]: In [14]: next(g)Out[14]: 10In [15]: next(g)---------------------------------------------------------------------------StopIteration Traceback (most recent call last) in ()----> 1 next(g)StopIteration: In [16]: 

该函数除了“屈服”作为参数传递的值外,什么也不做。但是,仅像“正常”函数那样调用函数不会产生返回值。如果可能,将使用一个参数实例化生成器函数,并将其保存在变量中g。现在,随着next()对生成器对象的调用清楚了,必须迭代生成器以生成值。并且,一旦产生其(单个)值,生成器便会耗尽-随后的调用next()会引发“ StopIteration”异常。如果我们在一个for循环中迭代了此生成器函数,则包含在中的底层迭代机制for将StopIteration优雅地处理该异常。

大多数Python文本使用循环语句介绍生成器,类似于以下代码:

In [19]: def countdown_gen(x): ...: count = x ...: while count > 0: ...: yield count ...: count -= 1 ...: In [20]: g = countdown_gen(5)In [21]: for item in g: ...: print(item) ...: 54321

但是我发现这可能导致控制流和控制权的混乱。必须理解的是,在循环内迭代时,生成器不会产生任何值,直到从客户端请求它们为止for。在for循环中,Python 隐式地调用next()了它从生成器对象获得的迭代器。也就是说,在for循环中,Python隐式地这样做:

In [32]: g = countdown_gen(5)In [33]: g_iter = iter(g)In [34]: next(g_iter)Out[34]: 5In [35]: next(g_iter)Out[35]: 4In [36]: next(g_iter)Out[36]: 3In [37]: next(g_iter)Out[37]: 2In [38]: next(g_iter)Out[38]: 1In [39]: next(g_iter)---------------------------------------------------------------------------StopIteration Traceback (most recent call last) in ()----> 1 next(g_iter)StopIteration: In [40]: 

当然,next()可以在生成器函数的迭代器上显式调用它,这有助于在Python解释器控制台上手动强制生成器函数进行迭代。

下图也可以帮助解释这些步骤。

这样,与闭包一样,Python的生成器函数在连续调用之间保持状态。或者,如PEP 255所述:

如果遇到yield语句,则函数的状态将被冻结,并且expression_list的值返回给.next()的调用方。“冻结”是指保留所有局部状态,包括局部变量的当前绑定,指令指针和内部评估堆栈:保存了足够的信息,以便下次调用.next()时,该函数可以就像yield语句只是另一个外部调用一样继续进行。

这种状态保留和值的懒散生成很难用这么小的琐碎示例来概念化,因此我尝试通过编码我认为可能有用的生成器函数使其更加具体。


用例— S3

Amazon的S3存储服务提供了一种相当简单且可扩展的方式,可以以非分层结构远程存储数据。关于S3的完整讨论不在本文的讨论范围之内,但是在吸引我探索是否可以在生成器函数中封装一些有用的S3资源访问功能之前,已经对S3进行了一些工作。

该boto3 Python库提供的API调用访问S3会话,资源和文件对象。以前我曾使用过download_file()API调用,但是正如预期的那样,这会将整个远程文件下载到一个人的当前工作目录中。如果您要在EC2实例上的Docker容器中运行Python脚本,那就很好,但是对于我目前的工作,我在MacBook Air上运行脚本,因此我很想找到一种避免使用本地存储的方法,仍然能够访问远程文件。

幸运的是,boto3库允许通过对象API访问文件资源的“流主体”。这似乎是生成器函数的理想候选者,因为文件对象仅应按需流传输(即,延迟)。

当然,可以直接使用这些API调用并直接在文件流上进行迭代。但是我认为包装访问文件流所需的所有S3客房整理可能会更优雅。尽管生成器会在调用之间保留状态,但我的建议是在类内部组合生成器函数,以管理S3会话状态。通过重载__iter__类中的方法,我可以使类可迭代。这样,我可以使我的S3类的行为类似于Python标准库中的文件对象。

我的此类代码如下:

import boto3class S3FileReader: """ class S3FileReader: Class to encapsulate boto3 calls to access an S3 resource and allow clients to stream the contents of the file iteratively, via a generator function: __iter__() """ def __init__(self, cfg, resource_key, bucket=None): """ __init__(self, cfg, bucket, resource_name): S3FileReader constructor initializes the S3 Session, gets the resource for a given bucket and key, obtains the resource's object, and obtains a handle to the object. Params: cfg: config.py file containing S3 crexentials bucket: name of the S3 bucket to access resource_key: key of the S3 resource (file name) """ try: if not bucket: bucket = cfg.bucket self._session = boto3.Session( aws_access_key_id=cfg.aws_access_key_id, aws_secret_access_key=cfg.aws_secret_access_key) self._resource = self._session.resource('s3') self._object = self._resource.Object(bucket, resource_key) self._handle = self._object.get() except Exception: raise S3FileReaderException('Failed to initialize S3 resources!') def __iter__(self): """ __iter__(self): Provide iteration interface to clients. Get the stream of our S3 object handle and produce results lazily for our clients from a generator function. yield statement yields a single line from the file. Returns: nothing. A StopIteration exception is implicitly raised following the completion of the for loop. """ if not self._handle: raise S3FileReaderException('No S3 object handle!') stream = self._handle['Body'] for line in stream: yield line def __enter__(self): """ __enter__(self): Implement Python's context management protocol so this class can be used in a "with" statement. """ return self def __exit__(self, exc_type, exc_value, exc_tb): """ __exit__(self, exc_type, exc)_value, exc_tb): Implement Python's context management protocol so this class can be used in a "with" statement. If exc_type is not None, then we are handling an exception and for safety should delete our resources """ if exc_type is not None: del self._session del self._resource del self._object del self._handle return False else: # normal exit flow return Trueclass S3FileReaderException(Exception): """ class S3FileReaderException(Exception): Simple exception class to use if we can't get an S3 File handle, or otherwise have an exception when dealing with S3. """ def __init(self, msg): self.msg = msg

毫无疑问,此类的代码比其提供的有限功能所必需的更为精细。但是它提供了少量的异常处理,此外,它实现了Python的上下文管理接口,因此可以像标准库的文件对象一样使用该类。这消除了对更多详细的try / except块的需要。该__exit__函数利用了免费对象删除功能-这背叛了我以前C++遵循类析构函数最佳实践的习惯;但它也明确表明了在对象清除时释放所有S3资源的意图-会话,资源和对象。boto3库似乎不支持close()方法。

在构造函数中完成必要的S3内部整理之后,该类提供了一个不错的接口,用于通过该__iter__方法迭代文件的流。客户代码可能希望在流上进行迭代时实现其他处理或逻辑。对他的小类的一个很好的增强将是添加一个过滤谓词,这样,__iter__如果用户知道他们只对数据的一个子集感兴趣,则该方法不必发出大文件的每一行。标准库itertools.dropwhile功能的使用在这里可以很好地工作。

主要好处是S3FileReader类的客户端不必担心S3的内部维护和维护,只需要表示感兴趣的资源即可。即使该类在文件流上进行迭代以在__iter__方法中产生行,但是由类的客户端控制迭代和数据的产生。


结论

Python生成器函数在标准库中得到了广泛使用,并为程序员提供了用于推迟计算,节省时间和空间的强大工具。

python文件之间的相互调用_「Python 系列」 Python 生成器函数详解相关推荐

  1. python文件之间的相互调用_一行 Python 代码能实现什么丧心病狂的功能?

    python一直被病垢运行速度太慢,但是实际上python的执行效率并不慢,慢的是python用的解释器Cpython运行效率太差. "一行代码让python的运行速度提高100倍" ...

  2. python文件之间的相互调用_用Python创建功能模块——截取字符串模块

    用Python创建模块 本篇文章小编给大家分享在Python如何创建属于自己的模块,实现更多的功能! 一.模块 模块是什么?简单来说,当程序的功能很多.代码量很大时,我们可以编写一个实现一定功能的py ...

  3. python文件都是脚本吗_脚本语言系列之Python | Python文件IO

    Python是做自动化工作首选的一门语言,与更复杂的语言相比,Python 非常便于快速学习.语法很少,具有良好的 可读性,即使测试工程师们没有丰富的编码经验,也能快速学会:Python 的极简风格对 ...

  4. python对excel某一列去重-「总结篇」Python中所有的Excel操作技巧

    原标题:「总结篇」Python中所有的Excel操作技巧 Python对于Excel的操作是多种多样的,掌握了相关用法就可以随心所欲的操作数据了! 操作xls文件 xlrd(读操作): import ...

  5. java8 lambda maplist排序_「java8系列」流式编程Stream

    前言 「Java8系列」神秘的Lambda 「Java8系列」神奇的函数式接口 继上两篇之后,本文已经java8系列的第三篇了.本篇文章比较长,但我希望大家都能认真读完.读不完可以先收藏,在找时间读. ...

  6. python嵌套类(内部类相互调用)_核心解密Python函数在(类与函数之间)和(类与类之间)互相调用...

    image.png 一.类与函数之间 首先来看一个函数间的调用 类方法: #实现类中函数之间互相调用 #下面一个学生的简单自我介绍为例子 __metaclass__=type #自由的.动态的修改/增 ...

  7. python文件之间如何互相通信_不同的類和.py文件之間的python通信

    我試圖在我的程序中使用mvc-模型.我使用pubsub在我的文件之間進行通信.我想知道是否有另一種溝通方式,而不是使用pubsub?不同的類和.py文件之間的python通信 我的具體問題:我有2個文 ...

  8. python在主线程上下文执行_线程进程系列*(Python)

    一 ,线程的两种调用方式 threading 模块建立在thread 模块之上.thread模块以低级.原始的方式来处理和控制线程,而threading 模块通过对thread进行二次封装, 提供了更 ...

  9. java8堆内存模型_「GC系列」JVM堆内存分代模型及常见的垃圾回收器

    1. 内存分代模型 为什么要说JVM的内存分代模型呢,因为内存分代和垃圾回收器的运行是有关系的. 现在大部分用到的垃圾回收器在逻辑上是分代的,除了G1之外的其他垃圾回收器在逻辑上和物理上都是分代的. ...

最新文章

  1. kvm cpu的亲和性绑定配置
  2. 大型网站技术架构(3):WEB 前端性能优化
  3. php 挂机,用php实现qq挂机
  4. Java学习小程序(5)猜数字游戏
  5. 中国双层超级电容器市场趋势报告、技术动态创新及市场预测
  6. 工程与ArcGIS api for javascript不在同一个tomcat情况下跨域问题
  7. 证明的思路 —— 数形结合
  8. linux内核分析及应用 -- 输入输出(下)
  9. 有哪些 pmp 资料分享?
  10. ShuffleNet在Caffe框架下的实现
  11. android农历日历,万年历农历日历app
  12. jmeter+PerfMon Metrics Collector监控(ServerAgent)
  13. Integrated Electrical Test Vehicle Co-designed with Microfluidics for Evaluating the Performance of
  14. ubuntu分区时主分区和逻辑分区怎么分
  15. A - Artwork ( 并查集 )
  16. “玩具租赁/销售平台”类产品进化发展的可能性浅析
  17. 华为业务板块_华为三大业务介绍
  18. 第32篇 网络(二)HTTP
  19. IaaS、PaaS、SaaS、DaaS——各种云服务模式一览
  20. PTA - 数据库合集51

热门文章

  1. android蓝牙hfp client使用例
  2. 深度学习自学(二十二):推理框架-MNN
  3. 人脸方向学习(十四):Face Anti-Spoofing-人脸活体检测-双目红外活体检测解读
  4. VALSE学习(十二):视频时序建模和动作识别
  5. 不同发行版linux介绍
  6. java web网上书城_javaweb网上书城项目
  7. 贝叶斯数据分析_Python数据分析 · 朴素贝叶斯详解
  8. python学什么方向就业好_学python就业都有哪些方向
  9. cad细等线体不显示_CAD技法大全,如何在CAD中解决好部分图元无法正常显示的问题 ?...
  10. plsql怎么导出几十w的数据到csv_Greenplum数据库使用总结(干货满满)初级使用