python-yield(yield)有什么作用?

问题描述 投票:0回答:3

从 python 2.5 开始,可以将

send()
throw()
close()
转换为生成器。在定义的生成器内部,可以通过执行以下操作来“捕获”发送的数据:

def gen():
    while True:
        x = (yield)
        if x == 3:
            print('received 3!!')
            break
        else:
            yield x

我想要玩的是做类似的事情:

def gen2():
    while True:
        yield (yield)

注意到它是一个合法的生成器,可以做一些事情.. 我想弄清楚的第一件事是:

这样写有什么好的用法吗?

当做类似的事情时:

g = gen2()
next(g)
g.send(10) # output: 10
g.send(2) # output: nothing
g.send(3) # output: 3
g.send(44) # output: nothing

为什么每一秒“发送”没有任何作用?

python generator yield
3个回答
10
投票

yield (yield)
首先从内部
None
产生
yield
。然后它从
send
next
接收值。内部
yield
评估接收到的值,外部
yield
立即生成该值。


每个

yield
从概念上讲都有两部分:

  1. 将值传输给
    send
    next
    的调用者。
  2. 从下一个
    send
    next
    调用接收值。

类似地,每个

send
next
在概念上都有两部分:

  1. 将值传输到生成器当前暂停的
    yield
    表达式。 (对于
    None
    ,该值为
    next
    。)
  2. 从下一个
    yield
    表达式接收值。

系统中最令人困惑的部分可能是这些部分是交错的。

yield
的两个部分对应于
send
next
的两个不同调用,
send
next
的两个部分对应于两个不同的
yield

如果我们看一个简单的例子:

def gen():
    print('Not ran at first')
    yield (yield)

g = gen()  # Step 1
print(next(g))  # Step 2
print(g.send(1))  # Step 3
g.send(2)  # Step 4

事情是这样的:

Inside the generator                      Outside the generator

步骤1

                                          g calls gen()
g returns a generator object 
without executing the print
just yet statement.
                                          >>> g
                                          <generator object gen at 0x7efe286d54f8>

第2步

                                          next(g) sends None to g
g receives None, ignores it
  (since it is paused at the start
   of the function)

g prints ('not ran at first')

g executes the "transmit" phase
  of the inner yield, transmitting
  None
                                          next(g) receives None

第3步

                                          g.send(1) sends 1 to g
g executes the "receive" phase
  of the inner yield, receiving 1
g executes the "transmit" phase
  of the outer yield, transmitting 1
                                          g.send(1) receives 1 from g

第4步

                                          g.send(2) sends 2 to g
g executes the "receive" phase
  of the outer yield, receiving 2
g reaches the end of gen and raises
  a StopIteration
                                          g.send(2) raises the StopIteration
                                          from g

3
投票

yield
是一个表达式。 表达式的值是使用
.send
发送的任何内容的值,如果没有发送任何内容,则为 None (包括如果使用
next
代替
.send
)。
.send
是一个方法调用,因此当然也会返回一个值,该值是生成器生成的值。 换句话说,每次您
.send
时,都会产生一个值(可能是 None),而每次您
yield
时,都会发送一个值(可能是 None)。

这是一个简单的例子:

def gen():
    sent1 = yield 1
    print(sent1, "was sent")
    sent2 = yield 2
    print(sent2, "was sent")
    print("Reached end of generator")

g = gen()
print(next(g), "was yielded")
print(g.send("A"), "was yielded")
print(g.send("B"), "was yielded")
next(g)

# output
1 was yielded
A was sent
2 was yielded
B was sent
Reached end of generator
# StopIteration is raised here

在您的示例中,第一个

next
产生 None,因为第一个
yield
yield (yield)
中的内部产量(即括号中的那个)。 第一个
send
传递 10 作为此
yield
的值。 您
send
的每个后续值都会成为其中一个收益率的值。 您的某些
send
调用不产生输出的原因是内部收益未指定任何值,因此它产生
None
。 如上所述,当你调用
send
时,会产生一个值;在您的情况下,内部产量的该值为 None ,因此交互式提示中不会显示任何输出。 另一方面,外部收益率确实指定了一个值,即内部收益率的结果。 因此,当您将一个值放入内部
send
时,它将在下一次迭代时由外部
yield
产生。 (我假设您指的是交互式提示符处的输出;如果您将代码作为脚本运行,则根本不会有输出,因为您永远不会
yield
任何内容或以其他方式产生显式输出。)

这是另一个可能具有启发性的例子:

print

请注意,每次发送一个值时,它都会立即返回。  这是因为每个 
def gen(): yield (yield (yield (yield "WHOA"))) >>> g = gen() >>> next(g) 'WHOA' >>> g.send(1) 1 >>> g.send(2) 2 >>> g.send(3) 3 >>> g.send(4) Traceback (most recent call last): File "<pyshell#11>", line 1, in <module> g.send(4) StopIteration

都会产生更深层嵌套的

yield
的值。 每个
yield
“成为”发送的值,并立即由链中的下一个
yield
产生。 这将持续下去,直到所有收益都耗尽并引发 StopIteration。

之前已经问过类似的问题。 我的印象是,由于人们期望

yield

“只发送”一个值,因此往往会出现混乱。 但事实并非如此。 使用

send
推进生成器并产生下一个结果,就像使用
send
一样。 您可以将
next
视为等同于
next(gen)
    


0
投票
真的

很困惑”!反对KISS!)

© www.soinside.com 2019 - 2024. All rights reserved.