我需要通过list
传递巨大的tuple
/ *args
。
def f(*args): # defined in foreign module
pass
arguments = tuple(range(10000))
f(*arguments)
我想知道函数调用会发生什么。
它是否处理类似于任何位置变量的arguments
:在身体执行期间保存并按需访问?或者它甚至在正文执行之前迭代arguments
,扩展位置参数?还是别的什么?
答案 0 :(得分:13)
使用生成器的简单测试:
def gen():
print('Yielding 1')
yield 1
print('Yielding 2')
yield 2
print('Yielding 3')
yield 3
arguments = gen()
def f(*args):
pass
f(*arguments)
# Yielding 1
# Yielding 2
# Yielding 3
从输出中可以看出,传递*arguments
实际上将解包整个iterable,因为从技术上讲,您告诉Python使用*arguments
语法将iterable作为单独的参数传递。函数定义也使用*args
并不重要,这使得Python再次将参数打包回元组。
所以是的,你打开包装清单只是为了再次打包。您可以直接传递列表来避免这种情况。
答案 1 :(得分:8)
是的,*arguments
调用语法有来迭代arguments
iterable,原因有两个:
您传入的是列表,但函数中的*args
variable-size参数是一个元组。因此,必须在此处复制元素。
调用语法必须可用于任何函数,您可能拥有实际的位置参数,而不是*varargs
变量。
例如,如果函数签名是def f(foo, *args):
,那么第一个元素必须单独传递。
原则上,CPython可以针对function(*tupleargs)
调用中使用的元组的所有值最终在*varargs
参数中进行优化,并重新使用那个元组。但是,这实际上并非所有 共同,而且没有人这样做。
请注意,对于**kwargs
调用语法, mutability 所增加的挑战使得共享对象的使用非常糟糕;你有来创建所用dict的副本,因为否则函数或调用者可以使用另一个引用中反映的更改来改变该字典。