元组作为函数参数

22

在Python中,元组(在代码块中)由逗号定义;括号不是必需的(如下所示)。因此,这三个表达式都是等价的:

a, b = 1, 2
a, b = (1, 2)
(a, b) = 1, 2
如果我定义一个函数
def f(a, b):
    print(a, b)

这样称呼是可行的:

f(2, 3)

这不会:

f((2, 3))
# TypeError: f() missing 1 required positional argument: 'b'

当元组作为函数参数时,Python会如何对待它们?在这种情况下,括号是必要的(我理解为什么会这样,而且我很高兴Python能够以这种方式工作!)。

我的问题是:当元组作为函数参数时,Python会如何对待它们。


2
你可能想要查看这个这个PEP-3113 - Sheldore
3个回答

32

为方便起见,Python将根据需要构造一个临时元组用于赋值语句。因此,在数据移动时,您的所有三个赋值语句都完全相同。

函数调用不是赋值语句; 它是一个引用映射。 因此,语义是不同的。

如果要让Python将您的元组解包为两个单独的参数,请使用*运算符:

f(*(2, 3))

一个函数调用不是一个赋值语句,它是一个引用映射。谢谢提醒!(我应该早就学会了!) - hiro protagonist
2
"Python构建一个临时元组" - 从概念上讲,这是Python所做的,但实际上它只是将对象推入堆栈并再次弹出。永远不会创建元组。尝试查看执行a,b = b,a的函数的dis.dis输出。 - Dunes

12

元组的行为类似于不可变的列表。你使用括号来表示它们可能会让人感到困惑,但这更多是一种巧合——这是因为括号用于将东西分组在一起并减少歧义。

当你调用一个函数时,你并没有提供一个元组。你提供的是参数。元组可以是一个参数,但只有一个——它只是一个类型为 tuple 的变量。

你可以使用以下符号将元组(或列表)展开成一系列参数:

tup = (2, 3)
f(*tup)
# expand the tuple (2,3) into a series of arguments 2, 3

您也可以使用字典,只需将 * 替换为 **

my_dict = {"arg1": 1, "arg2": 2}
f(arg1=my_dict["arg1"], arg2=my_dict["arg2"])
f(**my_dict)   # these are equivalent
另一方面,函数可以接受任意数量的参数(类似于其他语言中对printf()调用的方式)。例如:
def g(*args):
    print("I got this many arguments:", len(args))

在这里,如果您执行type(args),您将获得tuple,如果您执行type(*args),则会出现错误。这是因为在函数头中,*的作用恰恰相反:它将给定给函数的参数打包成单个元组,以便您可以使用它们。请考虑以下内容:

g(2, 3)  # 2 arguments, both integers
g((2, 3)) # 1 argument, a tuple
g(*(2, 3)) # 2 arguments, both integers

简而言之,

  • 函数的构建方式是使其能够接收任意数量的参数
  • *** 运算符可以将元组/列表/字典进行解包并传递给函数,也可以将函数返回值打包成元组/列表/字典。
  • 单个元组/列表/字典在其他情况下只是一个单独的变量。

1
我觉得这并没有真正回答他的问题,他问的是为什么 func(2, 3) 中的 2、3 不是元组。如果括号是可选的话,它应该是一个元组。 - RemcoGerlich
因为这就是语言的定义。元组是一种数据类型,就像任何其他类型的变量一样。函数接受一个参数序列(各种类型的参数)-而不仅仅是一个包含其余参数的参数。虽然许多函数(例如“线程”内容)使用元组来指定要提供给函数的参数,但这是出于必要性(将一组参数作为变量提供)。 - Green Cloak Guy

6
事实上,在Python中,圆括号被用于多种不同的事情——调用函数、创建元组(不仅仅是逗号很重要,还有空元组()),以及在表达式中改变优先级。
在解释它们的含义存在歧义的情况下(例如你的示例f(2,3)可能既是带两个参数的函数调用,也可能是带一个参数且该参数为元组的函数调用),语言必须做出选择。
如果Python解析器将其解析为元组,那么将无法使用具有多个参数的函数。如果Python解析器将其解析为两个参数,则无法传递字面量元组而不使用圆括号。
显然,第一种限制更大,因此选择了第二种方式。
另一个示例是只有一个元素的元组——(1+2)是产生数字3的表达式,还是一个只有一个元素3的元组? 如果是后者,则无法使用圆括号在表达式中表示优先级((3 + 4)* 5 vs 3 +(4 * 5))。 因此,决定对于包含一个元素的元组,在第一个元素后需要加上逗号((3,))。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接