generator * / splat 解包是如何工作的?

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

假设我有一些函数

f
将接受可变数量的参数:

def f(*args):
    for a in args:
        print(a)

下面我通过三种方式调用这个函数:

案例一:传入一个生成器表达式没有括号:
f(l for l in range(5))
>>> <generator object <genexpr> at 0x1234>
案例2:传入生成器表达式with括号:
f((l for l in range(5)))
>>> <generator object <genexpr> at 0x1234>
案例 3:传入带有括号的生成器表达式 ,然后用
*
aka splat 展开它:
f(*(l for l in range(5)))
>>> 0
>>> 1
>>> 2
>>> 3
>>> 4

这是我的问题:

  1. 相等性测试,
    f(l for l in range(5)) == f((l for l in range(5)))
    返回:
    <generator object <genexpr> at 0x7f8b2a5df570>
    <generator object <genexpr> at 0x7f8b2a5df570>
    True
    
    这向我表明生成器表达式周围的括号实际上没有做任何事情。对吗?
  2. 案例 1 和案例 2 发生了什么?单独调用时,输出表明它创建了一个生成器。此外,当在上面的问题 2 中测试相等性时,虽然我期望一个简单的
    True
    /
    False
    输出,但它无法帮助自己并向我展示它为每个函数调用创建了一个生成器对象(? ).也就是说,如果我将输出分配给一个新变量,即
    f2 = f(l for l in range(5))
    f2
    NoneType
    f2.__next__()
    抛出错误(
    TypeError: 'NoneType' object is not an iterator
    ),向我表明我刚刚创建的生成器不能被分配给一个变量。
  3. 案例3中的括号是否只是为了打包我的表达式以便它可以
    *
    展开?换句话说,括号是否只是因为口译员无法理解
    *l for l in range(5)
  4. 情况3,操作顺序是什么?
  5. 接上一个问题——在案例3中,
    f()
    “看到”了什么?
  6. 描述案例 3 的正确用语是什么? “扩展生成器并将其传递给函数”?我上面的任何措辞都不正确吗?

上下文:

如果有帮助,我正在阅读

asyncio
上的这个 Real Python 教程,他们在其中反复使用生成器扩展(?),例如:

async def main():
    res = await asyncio.gather(*(makerandom(i, 10 - i - 1) for i in range(3)))
    return res

...我收集等于:

async def main():
    res = await asyncio.gather(makerandom(0, 9),makerandom(1, 8), makerandom(2, 7))
    return res

...但这是我在 Python 职业生涯中第一次真正面对 生成器艺术.

python generator expansion splat
1个回答
1
投票

这向我表明生成器表达式周围的括号实际上没有做任何事情。对吗?

你是对的,括号没有做任何事情,但是

==
测试没有表明这一点;
f
总是返回
None
,所以
f(a) == f(b)
对于任意两个值
a
b
(允许
f
返回)。

此外,当在上面的问题 2 中测试相等性时,虽然我期望一个简单的 True/False 输出,但它无法帮助自己并向我展示它为每个函数调用创建了一个生成器对象(?)。

(l for l in range(5))
是一个创建生成器对象的表达式。
f
在调用它时打印生成器对象,因为生成器对象是
args
的一个元素,并且您告诉它打印
args
的每个元素。如果你尝试
f(1) == f(2)
它会打印 1 和 2.

案例3中的括号是否只是为了打包我的表达式以便它可以

*
展开?换句话说,括号是否只是因为口译员无法理解
*l for l in range(5)

是的。

情况3,操作顺序是什么?

  • (l for l in range(5))
    计算生成器表达式
  • f(*value)
    迭代
    value
    并将其转换为参数列表,使用该参数列表调用
    f

接上一个问题——在案例3中,

f()
“看到”了什么?

args == (1, 2, 3, 4)
。你可以
print(args)
看到这个;它只是一个元组。

描述案例 3 的正确用语是什么? “扩展生成器并将其传递给函数”?

当然。

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