python - Python 如何处理对生成器的多次 "send"调用?

标签 python python-3.x

有很多关于类似问题的好问题,例如

python generator "send" function purpose?

What does the "yield" keyword do?

让我们回到“发送”的定义:

Resumes the execution and “sends” a value into the generator function. The value argument becomes the result of the current yield expression. The send() method returns the next value yielded by the generator, or raises StopIteration if the generator exits without yielding another value. When send() is called to start the generator, it must be called with None as the argument, because there is no yield expression that could receive the value

但我觉得我遗漏了一些重要的东西。这是我的例子,有 3 个 send 调用,包括第一个带有 None 值的调用,只是为了初始化一个生成器:

def multiplier():
    while True:
        m = yield               # Line #3
        print('m = ' + str(m))  # Line #4
        yield str(m * 2)        # Line #5
        yield str(m * 3)        # Line #6

#------------------------

it = multiplier()

print('it.send(None): ')
print(str(it.send(None)))
print('--------------')

print('it.send(10): ')
print(it.send(10))
print('--------------')

print('it.send(100): ')
print(it.send(100))
print('--------------')

这是一个输出:

it.send(None): 
None
--------------
it.send(10): 
m = 10
20
--------------
it.send(100): 
30
--------------

问题:

  • 当我在第 5 行中使用 it.send(10) 时究竟发生了什么。要是我们 按照定义,生成器执行恢复。发电机 接受 10 作为输入值并在当前 yield 中使用它 表达式。在我的示例中是 yield str(m * 2),但是 m 设置为 10。什么时候发生的。是不是因为 第 3 行中 myield 之间的引用?

  • 第 6 行 it.send(10) 发生了什么,为什么输出仍然是 30? 这是否意味着我上一个问题中的引用仅有效 一次?

注意: 如果我更改了示例并在第 5 行和第 6 行之间添加了一行 m = yield,然后在 print(it. send(10)) - 在这种情况下,输出开始有意义:20 和 300

最佳答案

你的生成器函数有三个 yield表达式,但你丢弃了其中两个的值(第 5 行和第 6 行)。如果你对那里的值做了一些事情,你会看到 100在函数中使用。如果你继续运行你的例子,你第五次调用 send会导致生成器更新 m到一个新的值。

让我们看一下执行 send 的代码调用您的示例,并同时查看生成器在做什么:

it = multiplier()

此时生成器对象已经创建并保存到it .生成器代码尚未开始运行,它在函数代码的开头暂停。

print(str(it.send(None)))

这将开始运行生成器函数的代码。发送的值必须是 None否则你会得到一个错误。该函数永远不会看到该值。使用 next 更常见启动发电机,因为next(it)相当于it.send(None) .

生成器函数运行到第 3 行,其中第一个 yield出现。由于您没有产生任何特定值,因此来自 send 的返回值是None (打印出来)。

print(it.send(10))

这个值被发送到生成器并成为 yield 的值第 3 行的表达式。所以 10存储为 m ,代码在第 4 行打印出来。生成器函数继续运行到第 5 行,到达下一个 yield。表达。因为它产生 str(m * 2) , 调用代码得到 "20"并打印出来。

print(it.send(100))

100 值作为 yield 的值发送到生成器中在第 4 行。该值被忽略,因为您没有使用 yield作为一个表达,但作为一个陈述。就像把 100就其本身而言,这是完全合法的,但可能不是很有用。代码继续到第 5 行,在那里它产生 str(m * 3)。 , 或 "30" ,由调用代码打印。

这就是您的驱动代码停止的地方,但生成器仍然存在,您可以向它发送更多值(并取回更多值)。下一个值你send生成器也会被忽略,就像 100是,但之后的值最终会成为一个新的 m while 时的值生成器中的循环返回顶部,第 3 行 yield达到了。

我怀疑您对 send 感到有些困惑在这段代码中与您使用的事实有关 yield既作为表达又作为陈述。可能你不想两者都做。通常您要么关心所有发送到生成器的值,要么不关心其中任何一个。如果您想同时生成多个值(如 n*2n*3 ),您可以生成一个元组而不是单个项目。

这是您的代码的修改版本,我认为您可能更容易使用和理解它:

def multiplier():
    print("top of generator")
    m = yield # nothing to yield the first time, just a value we get
    print("before loop, m =", m)
    while True:
        print("top of loop, m =", m)
        m = yield m * 2, m * 3         # we always care about the value we're sent
        print("bottom of loop, m =", m)

print("calling generator")
it = multiplier()

print("calling next")
next(it)   # this is equivalent to it.send(None)

print("sending 10")
print(it.send(10))

print("sending 20")
print(it.send(20))

print("sending 100")
print(it.send(100))

关于python - Python 如何处理对生成器的多次 "send"调用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47851065/

相关文章:

python - 在 Python 中抓取字符串的元素 <script>

python - 从 pandas.core.series.Series 中提取数据时出现 KeyError

python - 应用于 groupby 的 first/count 返回空数据框

python - 让 Ansible 信任我在 macOS 和 CentOS 上的自定义 CA

python - 本地化日期时间列

python - 在 pylab.plot 中使用数据框列名作为标签

Python - 将数据拆分为 n 个分层部分

python - 数据类型 float32 for attr 'T' 不在允许值列表中 : int32, int64

python - 根据 Pandas 数据框中的键列减去列

python - 返回 lambda 的函数的类型注释