分享更有价值
被信任是一种快乐

Python生成器和协程怎么用

文章页正文上

本篇内容主要讲解“Python生成器和协程怎么用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Python生成器和协程怎么用”吧!你将如何生成任意长度的斐波那契数列?显然,你需要跟踪一些数据,并且需要以某种方式对其进行操作以创建下一个元素。你的第一直觉可能是创建一个可迭代的类,这不失是一个好方法。让我们开始,使用我们在前面几节中已经介绍过的内容:让我们把它变得更紧凑。如果你到目前为止一直在关注该系列,那么这里可能不会有任何惊喜。然而,对于像序列这样简单的事情,这种方法可能会让人觉得有点过头了。这种情况正是生成器的用途。生成器看起来肯定更紧凑——只有 9 行长,而类为 22 行——但它同样可读。关键是yield关键字,它返回一个值而不退出函数。yield在功能上与我们类中的__next__()函数相同。生成器将运行到(并包括)它的yield语句,然后在它做任何事情之前等待另一个__next_免费云主机、域名_()调用。一旦它得到那个调用,它将继续运行,直到它碰到另一个yield注意:看起来很奇怪的:=是 Python 3.8 中的新“海象运算符”,它分配并返回一个值。如果你使用的是 Python 3.7 或更早版本,则可以将这些语句分成两行(单独去赋值和写yield语句)。你还会注意到缺少raise StopIteration声明。生成器不需要它们;事实上,自PEP 479以来,他们甚至不允许他们这样做。当生成器函数自然终止或使用return语句终止时,StopIteration会在幕后自动触发。修订日期:2019 年 11 月 29 日曾经规定了yield不能出现在代码中try子句中的try-finally中。PEP 255定义了生成器语法,解释了原因:难点在于不能保证生成器会被恢复,因此不能保证 finally 块会被执行;这就违背finally的目的了。这在 PEP 342PEP 342中进行了更改,并在 Python 2.5 中完成。那么,为什么要讨论这样一个古老的变化呢?简单:直到今天,我的印象是yield无法出现在try-finally中.一些关于该主题的文章错误地引用了旧规则。你可能还记得 Python 将函数视为对象,生成器也不例外!在我们之前的示例的基础上,我们可以保存生成器的特定实例。例如,如果我只想打印斐波那契数列的第 10-20 个值怎么办?首先,我将生成器保存在一个变量中,以便我可以重用它。限制对我来说并不重要,所以我会使用大的限制。使用我的循环范围来更容易显示内容,因为这会使限制逻辑接近打印语句。接下来,我将使用循环跳过前 10 个元素。next()函数实际上是循环始终用于推进迭代的函数。在生成器的情况下,这将返回由yield返回的任何值。在这种情况下,由于我们还不关心这些值,我们只是将它们扔掉(对它们什么都不做)。顺便说一句,我也可以这样调用fib.__next__()——但我更喜欢采取的更简洁方法next(fib)。它通常取决于个人偏好。两者同样有效。我现在准备好从生成器访问一些值,但不是全部。因此,我仍将使用range(),并直接使用next()从生成器中检索值。这可以很好地打印出所需的值:还记得我们之前将限制设置为 100,现在已经完成了我们的生成器,但我们不应该直接离开并让它等待另一个next()调用!我们程序的其余部分处于空闲状态就会浪费资源(尽管很少)。相反,我们可以手动告诉我们的生成器我们已经完成了它。这将手动关闭生成器,就像它已经到达一个return语句一样。它现在可以由垃圾收集器清理。生成器允许我们快速定义一个在调用之间存储其状态的可迭代对象。但是,如果我们想要相反的结果:传递信息让函数耐心等待它得到它呢?Python为此提供了协程。对于已经有点熟悉协程的人,你应该明白我所指的是简单的协程(尽管我只是为了读者的理智而自始至终都在说“协程”。)如果你已经看过任何使用并发的 Python 代码,你可能已经遇到过它的小弟,原生协程(也称为“异步协程”)。现在,了解简单协程原生协程都被官方认为是“协程”,它们有很多共同的原则;原生协程建立在简单协程引入的概念之上。我们会在后续的文章中讨论async。同样,现在假设当我说“协程”时,我指的是一个简单的协程。想象一下,你想找到一堆字符串之间的所有共同字母,比如一本书籍中那些有趣的人物名字。你不知道有多少字符串,它们会在运行时输入,不一定是一次全部输入。显然,这种方法必须:可重复使用。有状态(到目前为止共有的字母。)本质上是迭代的,因为我们不知道我们会得到多少个字符串。普通的函数并不适合这种情况,因为我们必须一次将所有数据作为列表或元组传递,而且它们本身不存储状态。同时,生成器不能处理输入,除非是第一次调用。我们可以尝试新建一个类,尽管有很多模板。不管怎样,让我们从这儿开始,只是为了更好地掌握我们正在处理的内容。在我的第一个版本中,我将对传递给类的列表进行修改,因此我可以随时查看结果。如果我坚持使用类实现,我可能不会那样做,但它是实现我们目的最小的可行类了。此外,它在功能上与我们稍后将要编写的协程相同,这用来比较实现方法很有用。根据我的输出,这本数据特别喜欢带有 e、o、s、l 和 p 的名字。谁知道?我们可以使用协程完成相同的结果。让我们仔细看看这里发生了什么。乍一看,协程与函数并没有什么不同,但与生成器一样,yield关键字的使用就大不相同了。在协程中,yield它代表“等到你的输入,然后在这里使用它”。你会注意到两种方法之间的大多数处理逻辑是相同的。我们只是取消了类模板。我们存储协程的实例就像存储对象一样,只是为了确保每次向它发送更多数据时都使用相同的实例。类和协程之间的主要区别在于用法。我们使用协程的send()函数向协程发送数据:在我们这样做之前,我们必须首先调用(上面使用counter.send(None)的)或counter.__next__()。协程不能立即接收值;它必须首先运行它的所有代码,直到它的第一个yield.与生成器一样,协程在到达其正常执行流程的末尾或到达return语句时完成。由于在我们的示例中这些情况都没有发生的机会,所以我选择手动关闭协程:简而言之,使用协程:将其实例保存为变量,例如counter,用counter.send(None),counter.__next__()next(counter)输入协程,用counter.send()发送数据,如有必要,用counter.close()关闭它。还记得关于生成器的规则,不能将 yield放在语句的try子句中try-finally吗?但是这里不适用!因为yield在协程中的行为非常不同(处理传入数据,而不是传出数据),以这种方式使用它是完全可以接受的。生成器和协程也有一个throw()函数,用于在它们暂停的地方引发异常。你会从《错误和异常》一文中了解到,异常可以用作代码执行流程的正常部分。例如,假设你想将数据发送到远程服务器。你现在已经有一个连接对象,并且已使用协程通过该连接发送数据。在你的代码中,当检测到你已经失去了网络连接,但是由于你与服务器的通信方式,协程发送的所有数据都会毫无保留的被丢弃。考虑一下下面这个我已经删除的示例代码。(假设实际的连接逻辑本身不适合处理回退或报告连接错误。)运行该示例,我们看到前五个send()调用转到example.com,但后五个调用转到None。这显然是不行的——我们想抛出问题,然后开始将数据写到文件中,这样它就不会永远丢失。这就是throw()的作用。一旦我们知道我们已经失去了连接,我们就可以提醒协程这个事实,让它做出适当的响应。我们首先在协程中添加一个try-except:我们的使用示例只需要进行一处更改:一旦我们知道我们失去了连接,我们就使用sender.throw(ConnectionError)抛出异常:这样的话!现在我们会在协程收到警报后立即收到有关连接问题的消息,并将相关错误内容写入到本地文件,也就是所谓的日志文件。使用生成器或协程时,你不仅限于yield,你还可以使用yield from.例如,假设我想重写我的斐波那契数列以使其没有限制,并且我只想编码前五个值。在这种情况下,yield from暂时移交给另一个可迭代对象,无论它是容器、对象还是另一个生成器。一旦该可迭代对象结束,生成器就会启动并像往常一样继续运行。仅仅使用这个生成器,你不会知道它在部分时间内使用了另一个迭代器。它只是像往常一样工作。协程也可以以类似的方式进行切换。例如,在我们的 连接示例中,如果我们创建第二个协程来处理将数据写入文件会怎样?如果我们遇到连接错误,我们可以切换到在幕后使用它。你可能想知道:“我可以像从生成器中那样直接从协程中组合两个返回数据吗?”我在写这篇文章时也对此感到好奇,显然你可以。这一切都与识别函数何时被视为生成器而不是协程有关。关键很简单:实际上__next__()send(None)在协程中同样有效。我只需要观察协程何时开始接收None(当然是在初始启动之后)。由于我在word中存储了yield的结果,因此我可以用word变成None时作为跳出循环的判断条件。当我们将协程转化为生成器时,它需要在yield开始输出数据之前处理单个send(None)。在调用我们的协程时,我们在切换使用之前从未明确地send(None);Python 在后台执行此操作。另外,请记住协程/生成器仍然是一个函数。它只是在每次遇到yield时暂停。在我的示例中,我不能突然回去使用counter作为协程,因为没有执行流程可以让我回到word = yield。其实完全可以实现它,以便你可以来回切换,但如果它以牺牲可读性或变得过于复杂为代价,则可能不明智。到此,相信大家对“Python生成器和协程怎么用”有了更深的了解,不妨来实际操作一番吧!这里是云技术网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

相关推荐: 纯CSS如何实现多级导航联动

这篇文章主要介绍“纯CSS如何实现多级导航联动”,在日常操作中,相信很多人在纯CSS如何实现多级导航联动问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”纯CSS如何实现多级导航联动”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!…

文章页内容下
赞(0) 打赏
版权声明:本站采用知识共享、学习交流,不允许用于商业用途;文章由发布者自行承担一切责任,与本站无关。
文章页正文下
文章页评论上

登录

找回密码

注册