Ikr*_*a_5 60 python dictionary
构建字典时如下:
dict = { True: 'yes', 1: 'No'}
Run Code Online (Sandbox Code Playgroud)
当我在交互式Python解释器中运行它时,dict以这种方式表示:
dict = {True: 'No'}
Run Code Online (Sandbox Code Playgroud)
据我所知,由于类型强制,值True和1相等,因为在比较数字类型时,缩小的类型被扩展为另一种类型(布尔值是整数的子节点).因此,正如我从文档的理解,当我们进入True == 1的Python转换True到1并对它们进行比较.
我不明白为什么True选择它作为关键而不是1.
我错过了什么?
Ale*_*ley 41
字典被实现为哈希表,在这里添加键/值时有两个重要的概念:散列和相等.
要插入特定的键/值,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级别下创建字典的内容,这里有更多细节.
Python代码{True: 'yes', 1: 'No'}被解析为令牌并提供给编译器.鉴于语法,Python知道必须使用大括号内的值创建字典.将四个值加载到虚拟机的堆栈(LOAD_CONST)然后构建字典(BUILD_MAP)的字节代码排队.
四个常量值按照它们看到的顺序被推到堆栈的顶部:
'No'
1
'yes'
True
Run Code Online (Sandbox Code Playgroud)BUILD_MAP然后使用参数调用操作码2(Python计算两个键/值对).此操作码负责从堆栈中的项目实际创建字典.它看起来像这样:
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();
}
Run Code Online (Sandbox Code Playgroud)这里的三个关键步骤如下:
使用创建空哈希表_PyDict_NewPresized.小词典(只有几个项目,在这种情况下为2个)需要一个包含八行的表.
在for进入循环,开始于图2(在这种情况下)和倒计数至0.PEEK(n)是指向第n个项向下堆栈的宏.因此,在循环的第一次迭代中,我们将拥有
PyObject *key = PEEK(2*2); /* item 4 down the stack */
PyObject *value = PEEK(2*2 - 1); /* item 3 down the stack */
Run Code Online (Sandbox Code Playgroud)
这意味着,*key将是True和*value将'yes'通过第一循环.在第二个它将是1和'No'.
PyDict_SetItem在每个循环中调用以将当前*key和*value放入字典.这与您编写时调用的函数相同dictionary[key] = value.它计算密钥的哈希值,以找出在哈希表中首先查找的位置,然后,如果需要,将密钥与该行上的任何现有密钥进行比较(如上所述).Łuk*_*ski 31
基本前提是 - True并且1具有相同的哈希值并且彼此相等 - 这就是为什么它们不能成为哈希表中的单独键(技术上具有相同哈希值的不等对象可能 - 但哈希冲突会降低性能).
>>> True == 1
True
>>> hash(1)
1
>>> hash(True)
1
Run Code Online (Sandbox Code Playgroud)
现在,让我们考虑一个字节码:
import dis
dis.dis("Dic = { True: 'yes', 1: 'No'}")
Run Code Online (Sandbox Code Playgroud)
这打印:
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
Run Code Online (Sandbox Code Playgroud)
基本上发生的事情是dict文字被标记化为键和值,并且它们被推送到堆栈.之后将BUILD_MAP 2两对(键,值)转换为字典.
很可能是堆栈上的数据顺序(这似乎是由dict文字中的键的顺序决定的)和实现细节BUILD_MAP决定了产生的dict键和值.
似乎键值赋值是按照dict文字中定义的顺序完成的.赋值行为与d[key] = value操作相同,所以它基本上是:
key不在dict中(通过相等):add keydo dictvalue下key鉴于{True: 'yes', 1: 'No'}:
{}加 (True, 'yes')
(True, hash(True))== (True, 1)是dict中的新键1来'yes'加 (1, 'no')
1在dict(1 == True) - >字典中不需要新密钥1(True)的键的值'no'结果: {True: 'No'}
正如我评论的那样,我不知道这是否是Python规范的保证,或者它是否仅仅是CPython实现定义的行为,它可能在其他解释器实现中有所不同.
Rem*_*ich 21
True并且1是不同的对象,但它们都具有相同的值:
>>> True is 1
False
>>> True == 1
True
Run Code Online (Sandbox Code Playgroud)
这类似于两个可能具有相同值但仍存储在不同内存位置的字符串:
>>> x = str(12345)
>>> y = str(12345)
>>> x == y
True
>>> x is y
False
Run Code Online (Sandbox Code Playgroud)
第一个项目被添加到字典中; 然后当添加另一个时,该值已作为键存在.因此密钥更新,密钥值是唯一的.
>>> {x: 1, y: 2}
{"12345": 2}
Run Code Online (Sandbox Code Playgroud)
如果密钥已经存在于字典中,则它不会仅覆盖关键值的密钥.
我相信写作x = {True:"a", 1:"b"}是这样的:
x = {}
x[True] = "a"
x[1] = "b"
Run Code Online (Sandbox Code Playgroud)
当它到达x[1] = "b"钥匙时True已经在dict中了,为什么要改变呢?为什么不直接覆盖相关的值.
排序似乎是原因.代码示例:
>>> d = {True: 'true', 1: 'one'}
>>> d
{True: 'one'}
>>> d = {1: 'one', True: 'true'}
>>> d
{1: 'true'}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2660 次 |
| 最近记录: |