iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >只要3分钟,Python生成器原理详解
  • 478
分享到

只要3分钟,Python生成器原理详解

生成器详解原理 2023-01-31 07:01:05 478人浏览 独家记忆

Python 官方文档:入门教程 => 点击学习

摘要

翻译:你逗比segmentfault.com/a/1190000011330511这篇文章是对 500 Lines or Less 一书中高效爬虫一章的部分翻译,原文:How python Generators Work。建议结合《流畅的


翻译:你逗比

segmentfault.com/a/1190000011330511


这篇文章是对 500 Lines or Less 一书中高效爬虫一章的部分翻译,原文:How python Generators Work。建议结合《流畅的 Python》食用。


在掌握 Python 生成器之前,你必须了解常规 Python 函数的工作原理。通常,当一个 Python 函数调用子程序(subroutine)时,这个子程序将一直持有控制权,只有当子程序结束(返回或者抛出异常)后,控制权才还给调用者:


>>> def foo():

...     bar()

...

>>> def bar():

...     pass


标准的 Python 解释器是用 C 写的。解释器用一个叫做 PyEval_EvalFrameEx 的 C 函数来执行 Python 函数。它接受一个 Python 的堆栈帧(stack frame)对象,并在这个堆栈帧的上下文中执行 Python 字节码。这是 foo 的字节码:


>>> import dis

>>> dis.dis(foo)

  2           0 LOAD_GLOBAL              0 (bar)

              3 CALL_FUNCTioN            0 (0 positional, 0 keyWord pair)

              6 POP_TOP

              7 LOAD_CONST               0 (None)

             10 RETURN_VALUE


foo 函数将 bar 加载到堆栈中并调用它,然后从堆栈中弹出返回值,最后加载并返回 None。


当 PyEval_EvalFrameEx 遇到 CALL_FUNCTION 字节码的时候,它会创建一个新的 Python 堆栈帧,然后用这个新的帧作为参数递归调用 PyEval_EvalFrameEx 来执行 bar。


Python 的堆栈帧是分配在堆内存中的,理解这一点非常重要!Python 解释器是个普通的 C 程序,所以它的堆栈帧就是普通的堆栈。但是它操作的 Python 堆栈帧是在堆上的。除了其他惊喜之外,这意味着 Python 的堆栈帧可以在它的调用之外存活。(FIXME: 可以在它调用结束后存活)。要以交互方式查看,请从 bar 内保存当前帧:


>>> import inspect

>>> frame = None

>>> def foo():

...     bar()

...

>>> def bar():

...     global frame

...     frame = inspect.currentframe()

...

>>> foo()

>>> # The frame was executing the code for 'bar'.

>>> frame.f_code.co_name

'bar'

>>> # Its back pointer refers to t

>>> def bar():

...     global frame

...     frame = inspect.currentframe()he frame for 'foo'.

>>> caller_frame = frame.f_back

>>> caller_frame.f_code.co_name

'foo'


aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3Bu

现在这项技术被用到了 Python 生成器(generator)上——使用代码对象和堆栈帧这些相同的组件来产生奇妙的效果。


这是一个生成器函数(generator function):


>>> def gen_fn():

...     result = yield 1

...     print('result of yield: {}'.fORMat(result))

...     result2 = yield 2

...     print('result of 2nd yield: {}'.format(result2))

...     return 'done'

...


当 Python 将 gen_fn 编译为字节码时,它会看到 yield 语句,然后知道 gen_fn 是个生成器函数,而不是普通函数。它会设置一个标志来记住这个事实:


>>> # The generator flag is bit position 5.

>>> generator_bit = 1 << 5

>>> bool(gen_fn.__code__.co_flags & generator_bit)

True


当你调用一个生成器函数时,Python 会看到生成器标志,实际上并不运行该函数,而是创建一个生成器(generator):


>>> gen = gen_fn()

>>> type(gen)

<class 'generator'>


Python 生成器封装了一个堆栈帧和一个对生成器函数代码的引用,在这里就是对 gen_fn 函数体的引用:


>>> gen.gi_code.co_name

'gen_fn'


调用 gen_fn 产生的所有生成器都指向同一个代码对象,但是每个都有自己的堆栈帧。这个堆栈帧并不存在于实际的堆栈上,它在堆内存上等待着被使用


aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3Bu

堆栈帧有个 “last instruction”(FIXME: translate this or not?) 指针,指向最近执行的那条指令。刚开始的时候 last instruction 指针是 -1,意味着生成器尚未开始:


>>> gen.gi_frame.f_lasti

-1


当我们调用 send 时,生成器达到第一个 yield 处然后暂停执行。send 的返回值是 1,这是因为 gen 把 1 传给了 yield 表达式:


>>> gen.send(None)

1


现在生成器的指令指针(instruction pointer)向前移动了 3 个字节码,这些是编译好的 56 字节的 Python 代码的一部分:


>>> gen.gi_frame.f_lasti

3

>>> len(gen.gi_code.co_code)

56


生成器可以在任何时候被任何函数恢复执行,因为它的堆栈帧实际上不在堆栈上——它在堆(内存)上。生成器在调用调用层次结构中的位置不是固定的,它不需要遵循常规函数执行时遵循的先进后出顺序。生成器被是被解放了的,它像云一样浮动。


我们可以将 “hello” 发送到这个生成器中,它会成为 yield 表达式的值,然后生成器会继续执行,直到产出(yield)了 2:


>>> gen.send('hello')

result of yield: hello

2


现在这个生成器的堆栈帧包含局部变量 result:


>>> gen.gi_frame.f_locals

{'result': 'hello'}


从 gen_fn 创建的其他生成器将具有自己的堆栈帧和局部变量。


当我们再次调用 send 时,生成器将从它第二个 yield 处继续执行,然后以产生特殊异常 StopIteration 结束:


>>> gen.send('Goodbye')

result of 2nd yield: goodbye

Traceback (most recent call last):

    File "<input>", line 1, in <module>

StopIteration: done


异常有一个值,它是那个生成器的返回值:字符串 “done”。


--结束END--

本文标题: 只要3分钟,Python生成器原理详解

本文链接: https://www.lsjlt.com/news/191721.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 只要3分钟,Python生成器原理详解
    翻译:你逗比segmentfault.com/a/1190000011330511这篇文章是对 500 Lines or Less 一书中高效爬虫一章的部分翻译,原文:How Python Generators Work。建议结合《流畅的 ...
    99+
    2023-01-31
    生成器 详解 原理
  • Python 3 之 生成器详解
    ------ 生成器 ------------------------------------------------------------------如今Python对延迟提供更多的支持——它提供了工具在需要的时候才产生结果,而不是立即...
    99+
    2023-01-31
    生成器 详解 Python
  • 一文详解Python中生成器的原理与使用
    目录什么是生成器迭代器和生成器的区别创建方式生成器表达式基本语法生成器函数yield关键字yield和returnyield的使用方法生成器函数的基本使用send的使用可迭代对象的优...
    99+
    2024-04-02
  • 详解Python中迭代器和生成器的原理与使用
    目录1.可迭代对象、迭代器1.1概念简介1.2可迭代对象1.3迭代器1.4区分可迭代对象和迭代器1.5可迭代对象和迭代器的关系1.6可迭代对象和迭代器的工作机制1.7自己动手创建可迭...
    99+
    2024-04-02
  • Python3.10 Generator生成器Coroutine原生协程详解
    目录引言协程底层实现业务场景结语引言 普遍意义上讲,生成器是一种特殊的迭代器,它可以在执行过程中暂停并在恢复执行时保留它的状态。而协程,则可以让一个函数在执行过程中暂停并在恢复执行时...
    99+
    2022-12-28
    Python生成器原生协程 Python Generator Coroutine
  • MyBatisPlus代码生成器的原理及实现详解
    目录一、代码生成器原理分析二、代码生成器实现一、代码生成器原理分析 我们在观察之前写的代码的时候,会发现很多重复的内容。  一个Book模板,,只需要把红色部分的内容全部...
    99+
    2022-11-13
    MyBatisPlus代码生成器原理 MyBatisPlus代码生成器实现 MyBatisPlus 代码生成器
  • python迭代器,生成器详解
    目录迭代器 生成器 总结迭代器 聊迭代器前我们要先清楚迭代的概念:通常来讲从一个对象中依次取出数据,这个过程叫做遍历,这个手段称为迭代(重复执行某一段代码块,并将每一次迭代得到的结...
    99+
    2024-04-02
  • Python 生成器yield原理及用法
    目录前言1. 什么是yield2. yield 原理3. yield 常见用法总结前言 在Python中存在两种好用的功能迭代器与生成器。当我们刚开始接触到时,会简单的认为迭代只不过...
    99+
    2024-04-02
  • 详解Python生成器和基于生成器的协程
    目录一、什么是生成器二、基于生成器的协程三、协程的注意点四、协程装饰器五、python3原生协程一、什么是生成器 Generator 1.生成器就是可以生成值的函数 2.当一个函数里...
    99+
    2024-04-02
  • 彻底理解 Python 生成器
    1. 生成器定义 在Python中,一边循环一边计算的机制,称为生成器:generator。   2. 为什么要有生成器 列表所有数据都在内存中,如果有海量数据的话将会非常耗内存。 如:仅仅需要访问前面几个元素,那后面绝大多数元素占用的空...
    99+
    2023-01-30
    生成器 Python
  • Python中的生成器原理是什么
    这篇文章主要介绍“Python中的生成器原理是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Python中的生成器原理是什么”文章能帮助大家解决问题。什么是python生成器生成器是一种特殊的迭...
    99+
    2023-07-06
  • python中生成器的原理是什么
    这篇文章将为大家详细讲解有关python中生成器的原理是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。Python的优点有哪些1、简单易用,与C/C++、Java、C# 等传统语言相比,...
    99+
    2023-06-14
  • Python异步之生成器示例详解
    目录正文1. 什么是异步生成器1.1. Generators1.2. Asynchronous Generators2. 如何使用异步生成器2.1. 定义2.2. 创建2.3. 一步...
    99+
    2023-03-22
    Python 异步生成器 Python 异步
  • 人工智能TextGeneration文本生成原理示例详解
    目录承上启下原理选取预测的下一个字符的三种方式训练总结案例承上启下 上一篇文章我们介绍了 RNN 相关的基础知识,现在我们介绍文本生成的基本原理,主要是为了能够灵活运用 RNN 的...
    99+
    2023-01-05
    人工智能Text Generation Text Generation文本生成
  • Reduxsaga异步管理与生成器详解
    目录redux-saga生成器封装可执行生成器安装基本使用写法一写法二使用多个sagaredux-saga 在学习它之前先了解es6生成器 生成器 关键字:yield next() ...
    99+
    2023-02-06
    Redux saga异步管理 Redux生成器
  • 详解JS前端使用迭代器和生成器原理及示例
    目录正文for of 是干什么用的可迭代对象是什么?生成器和迭代器的关系。让非迭代对象也可以使用for of 进行遍历for循环和for in的关系总结正文 生成器和迭代器这两个东...
    99+
    2023-02-21
    JS前端迭代器生成器 JS迭代器生成器
  • 使用redis生成唯一编号及原理示例详解
    在系统开发中,保证数据的唯一性是至关重要的一件事,目前开发中常用的方式有使用数据库的自增序列、UUID生成唯一编号、时间戳或者时间戳+随机数等。 在某些特定业务场景中,可能会要求我们...
    99+
    2024-04-02
  • python中的生成器、迭代器、装饰器详解
    一、装饰器 由于一个函数能实现一种功能,现在想要在不改变其代码的情况下,让这个函数进化一下,即能保持原来的功能,还能有新的"技能",怎么办? 现已经存在一个自定义...
    99+
    2024-04-02
  • Python元类与迭代器生成器案例详解
    1.__getattr__和__getattribute__魔法函数 __getattr__是当类调用一个不存在的属性时才会调用getattr魔法函数,他传入的值item就是你这个调...
    99+
    2024-04-02
  • python中的迭代器,生成器与装饰器详解
    目录迭代器生成器装饰器总结迭代器 每一个可迭代类内部都要实现__iter__()方法,返回一个迭代类对象,迭代类对象则定义了这个可迭代类如何迭代。 for循环调用list本质上是是调...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作