在Python 3.5中进行select以在字典中比较它们时select键

在构build字典时如下所示:

dict = { True: 'yes', 1: 'No'} 

当我在交互式Python解释器中运行它时,字典是这样表示的:

 dict = {True: 'No'} 

我明白True1的值是相同的,因为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(); } 

这里的三个关键步骤如下:

  1. 一个空的哈希表使用_PyDict_NewPresized创build。 小字典(只有几个项目,比如2个)需要一个八行的表格。

  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'

  1. 在每个循环中调用PyDict_SetItem以将当前*key*value放入字典中。 这是你编写dictionary[key] = value时调用的函数。 它计算密钥的散列值,以计算散列表中首先查找的位置,然后根据需要将该密钥与该行上任何现有密钥(如上所述)进行比较。

基本的前提是 – True1有相同的散列,并且彼此相等 – 这就是为什么它们不能在哈希表中分开的键(技术上具有相同哈希的不等对象可能 – 但散列冲突会降低性能)。

 >>> 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'}

  1. {}开始
  2. (True, 'yes')

    1. True不是在dict – > (True, hash(True)) == (True, 1)是字典中的新键
    2. 更新键值为1值为'yes'
  3. 添加(1, 'no')

    1. 1是字典( 1 == True ) – >在字典中不需要新的键
    2. 更新键值等于1值( True ),值为'no'

结果: {True: 'No'}

正如我所评论的,我不知道这是否被Python规范所保证,或者如果它只是CPython实现定义的行为,在其他解释器实现中可能会有所不同。

True1是不同的对象,但它们都具有相同的值:

 >>> 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,所以你只需要将键的值设置为两次。