为什么非默认参数不能跟在默认参数之后?

172
为什么这段代码会抛出SyntaxError错误?
>>> def fun1(a="who is you", b="True", x, y):
...     print a,b,x,y
... 
  File "<stdin>", line 1
SyntaxError: non-default argument follows default argument

尽管以下代码段在运行时没有显示错误:
>>> def fun1(x, y, a="who is you", b="True"):
...     print a,b,x,y
... 

在Python中,定义函数的结构如下: def myfunction(position_arguments, *arguments, **keywords): .... - abhishekgarg
在关键字参数方面,这个问题没有明显的答案——为什么不能混合使用默认和非默认的关键字参数?它们都是关键字参数,所以它们的顺序并不重要。这是一种任意的限制。 - Tomasz Gandor
4个回答

208

所有必需参数都必须放在任何默认参数之前,因为它们是强制性的,而默认参数不是。从语法上讲,如果允许混合模式,则解释器无法确定哪些值匹配哪些参数,这是不可能的。如果参数没有按正确顺序给出,将引发SyntaxError

让我们看一下使用函数的关键字参数。

def fun1(a="who is you", b="True", x, y):
...     print a,b,x,y

假设允许如上述方式声明函数, 那么通过以上声明,我们可以进行以下(常规的)位置或关键字参数调用:

func1("ok a", "ok b", 1)  # Is 1 assigned to x or ?
func1(1)                  # Is 1 assigned to a or ?
func1(1, 2)               # ?

你将如何建议在函数调用中分配变量,以及如何与关键字参数一起使用默认参数。

>>> def fun1(x, y, a="who is you", b="True"):
...     print a,b,x,y
... 

参考 O'Reilly - Python 核心编程
这个函数在语法上使用了默认参数,以实现以上函数调用的正确性。 关键字参数调用对于提供无序位置参数非常有用,但是与默认参数结合使用时,它们也可用于“跳过”缺少的参数。


8
如果允许混合模式,解释器会无法确定哪些值与哪些参数匹配,从句法上讲是不可能的。Ruby允许在默认参数后面添加非默认参数。如果您想在函数调用中分配变量,只需使用func1(x=1, y=2)即可。 - weakish
5
为什么你写了“如果允许混合模式,则从句法上讲,解释器将无法确定哪些值与哪些参数匹配。”?我认为在PEP 3102中的算法即使允许 def foo(x=None, y) 也可以完全正常运行。因此,我仍然很好奇Python为什么不允许这样做。这是有一个真正的根本原因还是只因为“我们不允许它而不允许它”? - Jeroen Demeyer
嗨@JeroenDemeyer,抱歉之前没有看到你的评论。尝试通过PEP 3102中指定的位置参数绑定来运行下面的函数。def f(x=1, y, z=2, a=2, b):您会发现它无法将值绑定到变量。因此会出现语法错误。def foo(x=None, y)是一个更简单的示例可以运行 :) - Rahul Gautam
2
尝试使用def foo(a=1, b)通过foo(b=2)调用,并告诉我PEP抛出的位置,我猜这不是禁止在非默认参数之前使用默认参数的原因。 - droid192
使用这个函数定义 def func1(a="who is you", b="True", x, y): - 调用 func1(1) => @HanhanLi 期望是什么? => 如果将1分配给a,那么当我必须传递ab值以便为x发送值时,给出默认值的意义是什么? - Rahul Gautam
显示剩余5条评论

21
SyntaxError: non-default argument follows default argument

如果您允许这样做,那么默认参数将变得无用,因为您永远无法使用它们的默认值,因为非默认参数在其后面

然而,在Python 3中,您可以执行以下操作:

def fun1(a="who is you", b="True", *, x, y):
    pass

这使得xy成为关键字,因此您可以这样做:

fun1(x=2, y=2)

这样可以工作是因为不再存在任何歧义。请注意,您仍然不能执行fun1(2, 2)(那将设置默认参数)。


关键字参数是 Python 3 的一个很棒的特性。遗憾的是,将它们移植到 Python 2.6 和 2.7 的补丁已经过期了(http://bugs.python.org/issue1745)。 - Nick Chammas
3
这并不是真的:“如果你允许这样做,那么默认参数将变得无用,因为你永远无法使用它们的默认值,因为非默认参数在其后。” 例如:def foo(a=1, b):通过foo(b=2)调用 - Hanhan Li

16

让我在这里澄清两点:

  • 首先,非默认参数不应该跟随默认参数,这意味着你不能在函数中定义 (a=b,c),函数参数的定义顺序如下:
    • 位置参数或非默认参数,例如 (a,b,c)
    • 关键字参数或默认参数,例如 (a="b",r="j")
    • 仅限关键字参数,例如 (*args)
    • 可变关键字参数,例如 (**kwargs)

def example(a, b, c=None, r="w" , d=[], *ae, **ab):

(a,b)是位置参数

(c=None)是可选参数

(r="w")是关键字参数

(d=[])是列表参数

(*ae)是仅限关键字参数

(**ab)是可变关键字参数

  • 现在第二个问题是如果我尝试这样做: def example(a, b, c=a,d=b):

当保存默认值时,实参并未被定义,Python计算并保存默认值时,即在定义函数时进行。

c和d在此时没有被定义,只有在函数执行时才存在。

"a,a=b" 在参数中是不允许的。


1

必需参数(没有默认值的参数)必须放在起始位置,以便客户端代码只提供两个参数。如果可选参数放在起始位置,会令人困惑:

fun1("who is who", 3, "jack")

在你的第一个例子中,这会有什么作用?在最后一个例子中,x是“谁是谁”,y是3,a是“杰克”。

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