Python不接受关键字参数

9

我正在尝试让我的代码不接受关键字参数,就像一些内置函数也不接受关键字参数一样,但是,我无法做到。根据我的有限理解,这是我的思考:

def somefunc(a,b):
    print a,b

somefunc(10,20)

输出:

10 20

现在,当我运行以下代码时(我知道这不是函数定义中预期的关键字参数,但是从函数调用的方式来看,它似乎具有接受关键字参数的函数的语法):

somefunc(b=10,a=20)

输出:

20 10

我有两个问题:-

  1. 看到函数调用 somefunc(b=10,a=20),而不是函数定义,这似乎可以是只接受普通参数的函数调用,也可以是接受关键字参数的函数调用。解释器如何区分这两种情况?
  2. 是否可能将我们的任何函数转换为不接受关键字参数的形式,就像许多内置函数一样?

为什么要做这个呢? 我只是想检查一下自己是否能够做到这一点,以便在深入理解Python时不会遗漏任何内容。我不知道Python是否允许这样做。


为什么你想要这样做? - Burhan Khalid
1
我只是在检查我是否能够做到这一点,以便我不会错过深入理解Python的任何内容。 - GodMan
5个回答

8
您可以使用任意参数列表来实现这一点。请参见http://docs.python.org/tutorial/controlflow.html#arbitrary-argument-lists
例如:
def somefunc(*args):
    print args[0], args[1]

无需关键字调用:

somefunc(10,20)

提供:

10 20

使用关键字调用:

somefunc(a=10,b=20)

产生错误:
TypeError: someFunc() got an unexpected keyword argument 'a'

不过,为什么您想要这样做还不是很清楚。


2
+1 - 虽然不清楚为什么你想这样做。 - 只是因为你_能_做到这一点,并不意味着这样做就是可以的。 - Kimvais

7

广告1)如果Python函数使用somefunct(name=value, ...)这样的形式,则正确的名称将自动验证。 如果我记住了具有良好描述性参数名称的函数并测试它们是否被Python接受,那么我就不需要记住参数的确切标准顺序并进行“神经质”验证,每次使用时都要查看文档,因此通过命名参数调用优于非常长的位置参数列表。 因此,报告的行为是有充分根据的。(当然,短单字母参数“a,b”无助于防止错误。)

广告2)一些用C编写的众所周知的内置快速函数仅支持使用小数量的必需参数进行调用而不支持使用命名参数进行调用。 (例如hasattr

这是因为它们仅使用简单的头文件...(... PyObject *args),因此所有命名参数都会被自动拒绝。(Python永远无法反射C源代码中的参数名称。:-)

许多其他的C函数拥有一个标题为...(... PyObject *args, PyObject *kwds)的标题,并且通过实现更复杂的验证PyArg_ParseTupleAndKeywords和编写名称到文档字符串来支持指定的名称列表。


编辑:Python 3.8中通过新的函数参数语法/实现了仅限位置参数,以指示某些函数参数必须按位置指定并且不能用作关键字参数。

def somefunc(a, b, /):
    print(a, b)

我正在输入C语言中定义的函数,并且一直在寻找一种方法来让用户/静态分析器知道参数名称仅用于清晰度,而不被接受。三年后,我只是想让你知道,你的编辑帮了我大忙! - kgello

4
在不看函数定义的情况下,观察函数调用somefunc(b=10,a=20),它可能是一个接受普通参数或关键字参数的函数调用。解释器如何区分这两种情况?

我不确定这是否完全符合您的要求,但如果我理解正确,答案是“它不会”。无论如何定义函数的参数,您都可以使用关键字调用函数。如果您使用关键字语法(例如f(a=...))传递任何参数,Python会将相应的值绑定到函数定义中具有相同名称的参数。无论是否定义了默认值都没有关系。这两种函数定义不会创建不同的“参数类型”,只是一些参数可能有默认值,而其他参数可能没有。因此,无论您是使用def f(a)还是def f(a=2),您仍然可以使用f(a=...)。唯一的限制是您必须先传递/定义位置参数(例如,您不能执行def f(a=2, b)f(a=2, 3))。另请参见this answer

关于你问题的第二部分,我不知道有什么方法可以阻止Python函数接受命名参数的关键字传递值。如果你仅定义了<*args>则它将不接受关键字参数,但位置参数无法拥有单独的名称。

@BrenBarn: 无论函数参数如何定义,您都可以使用关键字调用该函数。我理解这一点。但是,我想说的是,仅仅通过查看函数调用somefunc(b=10,a=20),就很难判断函数定义是否期望通过名称ab传递2个不同的参数,还是函数期望关键字参数。是不是这样? - GodMan
@GodMan:我认为你在奇怪地使用“关键字参数”。你是指可变关键字参数(即**kwargs)吗?如果是这样,那么是的,你无法区分它们。但这不是“关键字参数”的意思;关键字参数只是通过关键字定义和/或传递的参数(即使它们是显式定义的)。定义为def f(a, b)def f(a=2, b=3)的函数都接受名称为ab的不同参数,在这两种情况下,您都可以通过位置或关键字传递这些值。 - BrenBarn
@Marcin:我已经编辑过了,澄清一下你不能有命名的位置参数,这些参数不接受通过关键字传递的值。 - BrenBarn
@BrenBarn 太棒了。不需要再点赞了,因为我一开始就给你的回答点了赞 ;) - Marcin

3

在Python中,关键字参数和位置参数之间没有这么强的区别。实际上,只需要简单地:

按照函数调用中列出的顺序解析位置参数 关键字参数可以以任何顺序添加,但不能在位置参数之前出现。

因此,如果你有以下内容:

def foo(x):
    pass

您可以做以下操作:

foo(1) # positional
foo(x=1) # keyword

但你无法执行 foo() 因为你没有提供默认值。

同样地,如果你有这个:

def foo(x,y='hello'):
    pass

foo(1) # this works fine
foo() # this is invalid, as an argument without a default isn't passed
foo(y='hello',1) # error - positional arguments come before keyword arguments

不要忘记阅读有关任意参数列表的内容。


0

我认为这是一个非常糟糕的Python特性,可能会导致代码难以维护和出现错误。假设你有一个函数:

def myurl(x, y):
    url = "{}:{}".format(x, y)
    return url

虽然启用将位置参数替换为关键字参数可能看起来是个好主意,但千万不要这样做。我很惊讶没有Python选项可以防止使用关键字参数调用此函数:

>> print myurl(x="localhost", y=8080)

如果有人发现该库以以下方式编写更易读:

def myurl(hostname, port):
    url = "{}:{}".format(hostname, port)
    return url

然后,使用关键字调用的代码myurl(x="localhost", y=8080)将失败:

TypeError: myurl() got an unexpected keyword argument 'x'

这意味着你的本地函数变量突然变成了全局变量!我希望这个问题在Python 3中能得到解决。


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