字典在Python中是用哈希表实现的,在添加键/值时有两个重要的概念:哈希和相等性。
为了插入特定的键/值,Python首先计算键的哈希值。这个哈希值用于确定表格的行,Python应该首先尝试将键/值放入其中。
如果哈希表的行为空,则可以插入新的键/值对到字典中,填充空行。
但是,如果该行已经有东西,则Python需要测试键的相等性。如果键相等(使用==
),则它们被视为相同的键,Python只需要更新该行上相应的值。
(如果键不相等,则Python会查看表格中的其他行,直到找到键或到达空行,但这与本问题无关。)
当你写{True: 'yes', 1: 'No'}
时,你告诉Python创建一个新的字典,然后用两个键/值对填充它。这些按从左到右的顺序处理:True: 'yes'
然后是1: 'No'
。
我们有hash(True)
等于1。键True
在哈希表的第1行处,字符串'yes'
是其值。
对于下一个键/值对,Python发现hash(1)
也是1,因此查看表格的第1行。那里已经有东西了,所以现在Python检查键是否相等。我们有1 == True
,因此1
被视为与True
相同的键,因此其对应的值更改为字符串'No'
。
这将产生一个只有一个条目的字典:{True: 'No'}
。
如果您想查看CPython 3.5的内部结构来了解在Python层面下创建字典的情况,这里提供更多详细信息。
The Python code {True: 'yes', 1: 'No'}
is parsed into tokens and given to the compiler. Given the syntax, Python knows that a dictionary must be created using the values inside the braces. Byte code to load the four values onto the virtual machine's stack (LOAD_CONST
) and then build the dictionary (BUILD_MAP
) is queued up.
The four constant values are pushed onto the top of the stack in the order that they're seen:
'No'
1
'yes'
True
The opcode BUILD_MAP
is then called with the argument 2
(Python counted two key/value pairs). This opcode is responsible for actually creating dictionary from the items on the stack. It looks like this:
TARGET(BUILD_MAP) {
int i;
PyObject *map = _PyDict_NewPresized((Py_ssize_t)oparg);
if (map == NULL)
goto error;
for (i = oparg; i > 0; i--) {
int err;
PyObject *key = PEEK(2*i);
PyObject *value = PEEK(2*i - 1);
err = PyDict_SetItem(map, key, value);
if (err != 0) {
Py_DECREF(map);
goto error;
}
}
while (oparg--) {
Py_DECREF(POP());
Py_DECREF(POP());
}
PUSH(map);
DISPATCH();
}
以下是三个关键步骤:
使用 _PyDict_NewPresized
创建一个空的哈希表。对于只有几个项的小字典(例如此处为2项),需要一个有8行的表格。
进入 for
循环,从2开始(本例中)向下计数到0。 PEEK(n)
是指向栈第n个项目的宏。因此,在循环的第一次迭代中,我们将获得
PyObject *key = PEEK(2*2);
PyObject *value = PEEK(2*2 - 1);
这意味着在第一次循环中,*key
将是True
,*value
将是'yes'
。在第二次循环中,它将是1
和'No'
。
在每个循环中都会调用PyDict_SetItem
函数将当前的*key
和*value
放入字典中。这与写dictionary[key] = value
时调用的函数相同。它计算键的哈希值以确定在哈希表中首先查找的位置,然后(如果需要)将键与该行上任何现有键进行比较(如上所述)。
1 == True
,第二个键覆盖了第一个键的值。然而,在此操作中没有必要重新编写键,只需要更新值即可。 - Martijn PietersTrue
仍然保留为键。这个问题是关于“为什么选择值的机制不同于选择键”的问题。 - Tadhg McDonald-JensenTrue
和1
的哈希值和比较结果相等,因此它们是相等的,True
是先存储的,所以就使用了True
。如果你调换字典中键值对的顺序,将键1
放在前面,那么结果就是1
。当添加一个新项时,如果已经在字典中出现过(因为True
和1
是一样的),那么就会覆盖之前的值,这是标准的字典行为。但是它不应该覆盖键,那没有意义。 - dwandersonkey: value
结构,但在构建时,它们会按顺序处理。@dwanderson 这就是你想说的吗?@Muposat 这是伴随着另一个问题而提出的一个问题,那个问题是为什么True
和1
被映射为True
,我回答了那个问题,因此一些程序员面临了这个问题。我想知道为什么 Python 3 选择了True
值。唯一愚蠢的问题是我们不问的问题。感谢大家帮助我理解。 - Ikra_5