函数调用中* args的执行模型是什么?

时间:2016-10-25 06:47:32

标签: python function

我需要通过list传递巨大的tuple / *args

def f(*args):  # defined in foreign module
    pass

arguments = tuple(range(10000))
f(*arguments)

我想知道函数调用会发生什么。

它是否处理类似于任何位置变量的arguments:在身体执行期间保存并按需访问?或者它甚至在正文执行之前迭代arguments,扩展位置参数?还是别的什么?

2 个答案:

答案 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的副本,因为否则函数调用者可以使用另一个引用中反映的更改来改变该字典。