Python中生成器与*(splat)解包的工作原理及疑问解答
函数定义
def f(*args): for a in args: print(a)
三种调用案例
案例1:传入不带括号的生成器表达式
f(l for l in range(5)) >>> <generator object <genexpr> at 0x1234>
案例2:传入带括号的生成器表达式
f((l for l in range(5))) >>> <generator object <genexpr> at 0x1234>
案例3:传入带括号并使用*(splat)解包的生成器表达式
f(*(l for l in range(5))) >>> 0 >>> 1 >>> 2 >>> 3 >>> 4
疑问解答
测试
f(l for l in range(5)) == f((l for l in range(5)))返回生成器对象及True,是否说明生成器表达式外的括号无实际作用?
是的。当生成器表达式作为函数的唯一位置参数传入时,Python允许省略外层括号,两种写法本质都是把生成器对象作为单个参数传入函数,效果完全一致。但如果生成器不是唯一参数,比如f(1, (l for l in range(5))),括号就不能省略,否则会触发语法错误。案例1、2中生成器为何无法被赋值给变量?调用输出显示创建了生成器,但赋值后变量为NoneType。
问题出在函数f的返回值:f没有定义return语句,默认返回None。如果你写g = f(l for l in range(5)),g接收的是函数的返回值而非传入的生成器。要获取生成器对象,需先赋值再传入:gen = (l for l in range(5)) f(gen)此时
gen就是生成器对象,而f(gen)的返回值依然是None。案例3中的括号是否仅为包裹表达式以便*解包?因
*l for l in range(5)无法被解释器识别?
没错。*操作符需要作用于一个完整的可迭代对象,l for l in range(5)作为生成器表达式,必须用括号包裹成一个整体才能被*正确解析。如果省略括号,Python会把*l当成独立表达式,后续的for会被误判为语法错误,无法正确识别生成器表达式。案例3的执行顺序是什么?
执行步骤如下:- 首先解析生成器表达式
(l for l in range(5)),创建生成器对象; - 然后
*操作符遍历生成器的所有元素,将每个元素作为独立的位置参数传入函数f; - 函数
f将这些参数收集为元组args,遍历元组并逐个打印元素。
- 首先解析生成器表达式
案例3中函数f接收到的内容是什么?
函数f的*args参数会把解包后的元素收集成一个元组,即args = (0, 1, 2, 3, 4),函数遍历这个元组完成打印操作。描述案例3的正确术语是什么?“展开生成器并传入函数”是否准确?
通俗表述“展开生成器并传入函数”是准确的;更正式的术语是使用*操作符解包生成器表达式,将其元素作为位置参数传入可变参数函数。
内容的提问来源于stack exchange,提问作者Collin

