在Python 3.5中进行select以在字典中比较它们时select键
在构build字典时如下所示:
dict = { True: 'yes', 1: 'No'}
当我在交互式Python解释器中运行它时,字典是这样表示的:
dict = {True: 'No'}
我明白True
和1
的值是相同的,因为types强制,因为当比较数字types时,缩小的types被扩展为另一种types(布尔值是整数的子元素)。 所以我从文档中了解到,当我们inputTrue == 1
Python将True
转换为1
,并将它们进行比较。
我不明白的是为什么selectTrue
作为一个关键而不是1
。
我错过了什么?
字典是作为散列表实现的,在这里添加键/值有两个重要的概念: 散列和相等 。
为了插入特定的键/值,Python首先计算键的哈希值。 这个散列值用于确定Python应该首先尝试放置键/值的表的行。
如果散列表的行是空的,那么很好:新的键/值可以插入到字典中,填充空行。
但是,如果该行中已经有东西,那么Python需要testing这些键是否相等。 如果键是相等的(使用==
),那么它们被认为是相同的键,Python只需要更新该行的相应值。
(如果键不相等,Python会查看表中的其他行,直到find键或到达一个空行,但这与此问题无关。
当你写{True: 'yes', 1: 'No'}
,你告诉Python创build一个新的字典,然后用两个键/值对填充它。 这些被处理从左到右: True: 'yes'
然后1: 'No'
。
我们有hash(True)
等于1. True
的关键在第1行的哈希表和string'yes'
是它的价值。
对于下一对,Python认为hash(1)
也是1,因此会查看表的第1行。 有一些东西已经存在,所以现在Python检查键是否相等。 我们有1 == True
所以1
被认为是与True
相同的键,所以它的对应值被改为string'No'
。
这导致一个字典有一个条目: {True: 'No'}
。
如果您想要观察CPython 3.5的内核,看看在表面Python级别下创build一个字典,下面是更多的细节。
-
Python代码
{True: 'yes', 1: 'No'}
被parsing为令牌并提供给编译器。 鉴于语法,Python知道必须使用大括号内的值创build字典。 字节代码将四个值加载到虚拟机的堆栈(LOAD_CONST
),然后生成字典(BUILD_MAP
)。 -
四个常数值按照它们被看到的顺序被推到栈顶:
'No' 1 'yes' True
-
然后用参数
2
调用操作码BUILD_MAP
(Python计算两个键/值对)。 这个操作码负责实际上从堆栈上的项目创build字典。 它看起来像这样 :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
创build。 小字典(只有几个项目,比如2个)需要一个八行的表格。 -
进入
for
循环,从2开始(在这种情况下),并倒计数到0.PEEK(n)
是一个macros指向堆栈中的第n个项目。 因此,在循环的第一个迭代中,我们将拥有
PyObject *key = PEEK(2*2); /* item 4 down the stack */ PyObject *value = PEEK(2*2 - 1); /* item 3 down the stack */
这意味着*key
将是True
并且在第一个循环中*value
将是'yes'
。 在第二个将是1
和'No'
。
- 在每个循环中调用
PyDict_SetItem
以将当前*key
和*value
放入字典中。 这是你编写dictionary[key] = value
时调用的函数。 它计算密钥的散列值,以计算散列表中首先查找的位置,然后根据需要将该密钥与该行上任何现有密钥(如上所述)进行比较。
基本的前提是 – True
, 1
有相同的散列,并且彼此相等 – 这就是为什么它们不能在哈希表中分开的键(技术上具有相同哈希的不等对象可能 – 但散列冲突会降低性能)。
>>> True == 1 True >>> hash(1) 1 >>> hash(True) 1
现在,我们来考虑一个字节码:
import dis dis.dis("Dic = { True: 'yes', 1: 'No'}")
这打印:
0 LOAD_CONST 0 (True) 3 LOAD_CONST 1 ('yes') 6 LOAD_CONST 2 (1) 9 LOAD_CONST 3 ('No') 12 BUILD_MAP 2 15 STORE_NAME 0 (Dic) 18 LOAD_CONST 4 (None) 21 RETURN_VALUE
基本上会发生什么是dict文字被标记为键和值,并被推送到堆栈。 之后, BUILD_MAP 2
两对(键,值)转换为字典。
最有可能的堆栈数据顺序(这似乎是由字典的BUILD_MAP
决定的)和BUILD_MAP
实现细节决定了生成的字典键和值。
似乎键值赋值按字面意义上的顺序完成。 赋值的行为与d[key] = value
操作相同,所以基本上是:
- 如果
key
不是字典(通过平等):添加key
做字典 - 存储
key
value
给定{True: 'yes', 1: 'No'}
:
- 以
{}
开始 -
加
(True, 'yes')
- True不是在dict – >
(True, hash(True))
==(True, 1)
是字典中的新键 - 更新键值为
1
值为'yes'
- True不是在dict – >
-
添加
(1, 'no')
-
1
是字典(1 == True
) – >在字典中不需要新的键 - 更新键值等于
1
值(True
),值为'no'
-
结果: {True: 'No'}
正如我所评论的,我不知道这是否被Python规范所保证,或者如果它只是CPython实现定义的行为,在其他解释器实现中可能会有所不同。
True
和1
是不同的对象,但它们都具有相同的值:
>>> True is 1 False >>> True == 1 True
这与两个可能具有相同值的string类似,但存储在不同的存储位置:
>>> x = str(12345) >>> y = str(12345) >>> x == y True >>> x is y False
首先将一个项目添加到字典中; 那么当另一个被添加时, 该值已经存在作为一个关键 。 所以密钥更新,密钥值是唯一的。
>>> {x: 1, y: 2} {"12345": 2}
如果密钥已经存在于字典中,它不会覆盖仅与相关值相关的密钥。
我相信写作x = {True:"a", 1:"b"}
是:
x = {} x[True] = "a" x[1] = "b"
到达x[1] = "b"
的时候,关键字True
已经在字典中了,为什么要改变呢? 为什么不只是覆盖相关的值。
sorting似乎是原因。 代码示例:
>>> d = {True: 'true', 1: 'one'} >>> d {True: 'one'} >>> d = {1: 'one', True: 'true'} >>> d {1: 'true'}
这是一个暧昧的声明。
逻辑: d = { True: 'no', 1: 'yes'}
当python评估expression式时,它是按顺序执行的,所以相当于这个。
d = dict() d[True] = 'no' d[1] = 'yes'
常量True是关键,但它的计算结果为1,所以你只需要将键的值设置为两次。