类构造函数和关键字参数 - Python如何确定哪一个是意外的?

DJG*_*DJG 20 python

假设我定义了以下类:

class MyClass(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y
Run Code Online (Sandbox Code Playgroud)

通常,可以通过以下方式之一实例化此类:

>>> MyClass(1,2)
<__main__.MyClass object at 0x8acbf8c>
>>> MyClass(1, y=2)
<__main__.MyClass object at 0x8acbeac>
>>> MyClass(x=1, y=2)
<__main__.MyClass object at 0x8acbf8c>
>>> MyClass(y=2, x=1)
<__main__.MyClass object at 0x8acbeac>
Run Code Online (Sandbox Code Playgroud)

这很好,花花公子.

现在,我们尝试使用无效的关键字参数,看看会发生什么:

>>> MyClass(x=1, j=2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __init__() got an unexpected keyword argument 'j'
Run Code Online (Sandbox Code Playgroud)

Python正确引发类型错误并抱怨unexpected keyword argument 'j'.

现在,我们可以尝试使用两个无效的关键字参数:

>>> MyClass(i=1,j=2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __init__() got an unexpected keyword argument 'i'
Run Code Online (Sandbox Code Playgroud)

请注意,两个关键字参数无效,但'i'在这种情况下,Python仅抱怨其中一个.

让我们颠倒无效关键字参数的顺序:

>>> MyClass(j=2, i=1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __init__() got an unexpected keyword argument 'i'
Run Code Online (Sandbox Code Playgroud)

这太有趣了.我改变了无效关键字参数的顺序,但Python仍然决定抱怨'i'而不是抱怨'j'.所以Python显然不会简单地选择第一个无效的关键来抱怨.

让我们再尝试一下:

>>> MyClass(c=2, i=1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __init__() got an unexpected keyword argument 'i'
>>> MyClass(q=2, i=1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __init__() got an unexpected keyword argument 'i'
Run Code Online (Sandbox Code Playgroud)

按字母顺序,我在之前i和之后尝试过一封信i,所以Python并没有按字母顺序抱怨.

这里有更多,这次是i在第一个位置:

>>> MyClass(i=1, j=2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __init__() got an unexpected keyword argument 'i'
>>> MyClass(i=1, b=2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __init__() got an unexpected keyword argument 'i'
>>> MyClass(i=1, a=2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __init__() got an unexpected keyword argument 'a'
Run Code Online (Sandbox Code Playgroud)

啊哈!我得到了抱怨,'a'而不是'i'.

我的问题是,当为类构造函数提供无效的关键字参数时,Python如何确定要抱怨哪一个?

Mar*_*ers 19

关键字参数存储在字典中,并且字典顺序(例如,基于散列算法,散列冲突和插入历史的任意)适用.

对于您的第一个示例,包含两个ij键的字典会i首先列出:

>>> dict(j=2, i=1)
{'i': 1, 'j': 2}
Run Code Online (Sandbox Code Playgroud)

请注意,{...}文字字符符号从右到左插入键,而关键字解析从左到右插入关键字(这是CPython实现细节); 因此dict()在上面的例子中使用了构造函数.

这一点很重要,当两个关键字散列到同一插槽,像ia:

>>> dict(i=1, a=2)
{'a': 2, 'i': 1}
>>> {'i': 1, 'a': 2}
{'i': 1, 'a': 2}
Run Code Online (Sandbox Code Playgroud)

字典输出顺序高度依赖于插入和删除历史以及特定的Python实现; 例如,Python 3.3引入了一个随机散列种子来防止严重拒绝服务向量,这意味着即使在Python进程之间,字典顺序也会完全不同.

  • 如果你想查看`CALL_FUNCTION`及相关操作码的相关CPython源代码,请查找[`_call_function_var_kw`](http://hg.python.org/cpython/file/3.3/Python/ceval.c#l2690) `ceval.c`(上面的链接是3.3分支)和`ext_do_call`在同一个文件中.基本上,调用者将名称和值压入堆栈中,然后将"CALL_FUNCTION"弹出. (5认同)