众所周知,Python函数最多可以有256个参数。我想知道的是当*args
和**kwargs
以以下方式展开时,这个限制是否适用:
items = [1,2,3,4,5,6]
def do_something(*items):
pass
我询问是因为,假设有情况下一个超过256项的列表会被展开成一组*args
或**kwargs
。
众所周知,Python函数最多可以有256个参数。我想知道的是当*args
和**kwargs
以以下方式展开时,这个限制是否适用:
items = [1,2,3,4,5,6]
def do_something(*items):
pass
我询问是因为,假设有情况下一个超过256项的列表会被展开成一组*args
或**kwargs
。
CALL_FUNCTION*
一系列操作码以提高性能和简单性(包含在3.6版本中),释放了操作码参数,只编码单个参数计数,并且#12844 删除了编译时检查,防止使用更多参数的代码被编译。
因此,在3.7及更高版本中,通过显式参数传递的参数数量现在完全没有限制,可以使用EXTENDED_ARG()
操作码,只受堆叠量大小的限制(因此现在受您的内存限制)。
>>> import sys
>>> sys.version_info
sys.version_info(major=3, minor=7, micro=0, releaselevel='alpha', serial=2)
>>> def f(*args, **kwargs): pass
...
>>> exec("f({})".format(', '.join(map(str, range(256)))))
>>> exec("f({})".format(', '.join(map(str, range(2 ** 16)))))
请注意,列表、元组和字典的元素数量被限制在sys.maxsize
个之内。因此,如果被调用函数使用*args
和/或**kwargs
通配符参数,那么这些参数是有限制的。
对于*args
和**kwargs
的调用语法(扩展参数),除了Python标准类型上的相同sys.maxint
大小限制外,没有其他限制。
在Python 3.7之前的版本中,CPython在调用中显式传递的参数上有255个的限制:
>>> def f(*args, **kwargs): pass
...
>>> exec("f({})".format(', '.join(map(str, range(256)))))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
SyntaxError: more than 255 arguments
这个限制是因为在Python 3.5之前,CALL_FUNCTION
操作码重载了操作码参数,用一个字节编码堆栈上的位置参数和关键字参数的数量。
CALL_FUNCTION
,它携带了一个长度为4个字节的op_arg
,但只使用了最不重要的两个字节。其中,最高位字节表示栈上关键字参数的数量,最低位字节表示栈上位置参数的数量。因此,你最多可以有0xFF == 255
个关键字参数或0xFF == 255
个位置参数。*args
和**kwargs
,因为使用该语法的调用会使用CALL_FUNCTION_VAR
、CALL_FUNCTION_KW
和CALL_FUNCTION_VAR_KW
字节码操作。对于这些操作码,堆栈由*args
的可迭代对象和**kwargs
的dict
组成。这些项直接传递给接收器,接收器按需展开它们。WFM
>>> fstr = 'def f(%s): pass' % (', '.join(['arg%d' % i for i in range(5000)]))
>>> exec(fstr)
>>> f
<function f at 0x829bae4>
更新: 正如Brian所指出的那样,限制在调用方这一侧。
>>> exec 'f(' + ','.join(str(i) for i in range(5000)) + ')'
Traceback (most recent call last):
File "<pyshell#63>", line 1, in <module>
exec 'f(' + ','.join(str(i) for i in range(5000)) + ')'
File "<string>", line 1
SyntaxError: more than 255 arguments (<string>, line 1)
另一方面,这个方法可行:
>>> f(*range(5000))
>>>
结论:不适用于展开的参数。
这似乎是编译源代码时的限制,因此可能仅适用于直接传递的参数,而不适用于*args或**kwargs。
相关的代码可以在ast.c中找到:
if (nargs + nkeywords + ngens > 255) {
ast_error(n, "more than 255 arguments");
return NULL;
}
f(a,b,c,d,e...)
,而不是定义方面,尽管它将计算位置参数 (a,b,c,d)
和关键字参数 (a=1, b=2, c=3)
。实际上,*args
和**kwargs
参数看起来只应将其视为一个参数来处理调用方面的目的。我尝试了一个包含4000个项目的列表,它可以正常工作。因此,我猜测它也适用于更大的值。