协程是一个类吗?

3

我正在按照一份指南学习协程

def grep(pattern):
    print("Looking for %s" % pattern)  # prime it(explain shortly)
    while True:
        line = (yield) # expression
        if pattern in line:
            print(line)

测试它

>>> g = grep("python")
>>> g.next()
Looking for python
>>> g.send("coroutine test")
>>> g.send("learning python")

似乎yield表达式的作用类似于functools.partial,除了它应该使用next()来初始化。

此时,def grep实际上是一个class grep,因为它首先初始化了一个生成器对象。

协程很难跟踪,我的理解是继续前进而没有进一步的副作用,因为Python将其命名为def而不是class,应该有她的原因。


2
type(grep)functiontype(grep('foo'))generator。我不确定你看到了什么与 functools.partial 相似的并行之处。 - timgeb
我猜在其实现细节中它是一个类,但由于语法考虑被命名为函数。@timgeb - AbstProcDo
@Sawajin,你不是在问实现细节吗?grepfunction的一个实例,而grep('foo')则是generator的一个实例。如果它们是类,那么它们将是type(或某个元类)的实例。 - timgeb
对于其他读者:这个问题似乎源自David Beazley的《关于协程和并发的好奇课程》(请参阅幻灯片)。 - Brad Solomon
1个回答

3
似乎yield表达式的作用类似于functools.partial,但是需要使用next()来预处理。我不确定你为什么会这么说,但我并没有立即看到相似之处。functools.partial旨在对可调用对象进行部分绑定,并允许您保存一些其他参数/关键字参数以便用户调用。partial()用于部分函数应用程序,它“冻结”函数的某些参数和/或关键字,从而产生一个具有简化签名的新对象。这与此生成器或任何生成器的情况并不相同。
协程很难跟踪,我的理解是,由于Python将其命名为def而不是class,因此可以继续进行而不产生进一步的副作用?
它们确实很棘手,但我不确定协程如何“像类一样”。协程是一种专门的生成器。生成器使用def定义,并能够暂停和恢复其执行。这描述了生成器,而不是类,并且仅将def替换为class可能在语法上无效,首先就会出现错误。
您可以将任何表达式(例如a = yield b)视为标记断点。当您调用next(g)时,它将前进直到遇到yield语句并停止。它将将结果值推送到调用堆栈中,但它将挂起其执行并停在那里,在您再次调用next()时可以恢复其执行(这是函数和生成器之间以及函数和协程之间的关键区别)。在第一次调用next()时,line为None。基本上,line = yield None。您将无法对此进行迭代,因为您无法说for pattern in None。在这种情况下,“预处理”意味着初始调用next(g)类似于g.send(None)。
现在,当您向生成器发送其他值时,它们将被分配给line,而pattern仍为“python”。如果在您.send()中找到了“python”,则会打印出来。
>>> g = grep("python")
>>> n = g.send(None)  # equiv to next(g); stop at line = (yield)
Looking for python
>>> n is None
True
>>> g.send("coroutine test")
>>> g.send("coroutine test")
>>> g.send("coroutine test")  # no match
>>> g.send("learning python") # match
learning python
>>> g.send("python3.7") # match
python3.7

在这种情况下,“priming”的意思可能是指对next(g)的初始调用类似于g.send(None)。这个洞见让我恍然大悟,谢谢。 - AbstProcDo
我认为它类似于一个偏函数,因为当grep被定义为一个标准函数时,g = grep(“python”)会将“python”冻结在g中,就像g = functools.partial(grep,“python”)一样。 - AbstProcDo
1
好的,我有点明白你的意思 - 但它并不需要被“冻结”,因为grep()只被调用一次。之后,它完全停留在while True循环中,而pattern保持不变。这对于以这种方式定义的任何函数都是正确的。 - Brad Solomon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接