写在前面:内容参照自《Effective Python》,其实你完全可以直接去看书,什么?你不想自己看书,那么你也可以关注我,我会不定期从书中挑出常用到的有效方法分享出来,这样你就可以一边刷头条,一边学习知识,岂不美哉。
正文
令函数接受可选的位置参数(由于这种参数习惯上写为 *args,所以又称为 star args,星号参数),能够使代码更加清晰,并能减少视觉杂讯(visual noise)。这是一种比喻,意思是使代码看起来不要太过杂乱,以强调其中的重要内容。
例如,要定义log函数,以便把某些调试信息打印出来。假如该函数的参数个数固定不变,那它就必须接受一段信息及一份含有待打印值的列表。
即便没有值要打印,只想打印一条消息,调用者也必须像上面那样,手工传人一份空列表。这种写法既麻烦,又显得杂乱。最好是能令调用者把第二个参数完全省略掉。
若想在 Python 中实现此功能,可以把最后那个位置参数前面加个*,于是,对于现在的 log 函数来说,只有第一个参数 message 是调用者必须要指定的,该参数后面,可以跟随任意数量的位置参数。函数体不需要修改,只需修改调用该函数的代码。
如果要把已有的列表,传给像 log 这样带有变长参数的函数,那么调用的时候,可以给列表前面加上*操作符。这样 Python 就会把这个列表里的元素视为位置参数。
接受数量可变的位置参数,会带来两个问题。
第一个问题是,变长参数在传给函数时,总是要先转化成元组(tuple)。这就意味着,如果用带有 * 操作符的生成器为参数,来调用这种函数,那么 Python 就必须先把该生成器完整地迭代一轮,并把生成器所生成的每一个值,都放人元组之中。这可能会消耗大量内存,并导致程序崩溃。
只有当我们能够确定输入的参数个数比较少时,才应该令函数接受 *arg 式的变长参数。在需要把很多字面量或变量名称一起传给某个函数的场合,使用这种变长参数,是较为理想的。该参数主要是为了简化程序员的编程工作,并使得代码更加易读。
使用 *arg 参数的第二个问题是,如果以后要给函数添加新的位置参数,那就必须修改原来调用该函数的那些旧代码。若是只给参数列表前方添加新的位置参数,而不更新现有的调用代码,则会产生难以调试的错误。
问题在于:上面的第二条log语句是以前写好的,当时的log函数还没有sequence参数,现在多了这个参数,使得7从values值的一部分,变成了message参数的值。这种bug很难追踪,因为现在这段代码仍然可以运行,而且不抛出异常。为了彻底避免此类情况,我们应该使用只能以关键字形式指定的参数(keyword-only argument),来扩展这种接受 *args 的函数。
要点
- 在def语句中使用*args,即可令函数接受数量可变的位置参数。
- 调用函数时,可以采用*操作符,把序列中的元素当成位置参数,传给该函数。
- 对生成器使用*操作符,可能导致程序耗尽内存并崩溃。
- 在已经接受*args参数的函数上面继续添加位置参数,可能会产生难以排查的bug。