密切注意
原文:丹·巴德译者:维密克斯
丹·巴德(Dan Bader)的一篇有趣的博文,让我们学习一下如何研究一个意想不到的Python现象。一个Python字典表达式难题
让我们探索下面这个晦涩难懂的Python字典表达式,看看Python解释器内部发生了什么。
#一个python难题:
#这个表达式计算出来后会是什么结果?
>。>。>。{True:'是',1:'否',1.0:'可能' }
有时候你会遇到一个很深的代码例子,哪怕只是一行代码,但是如果你想的足够多,它可以教会你很多关于编程语言的知识。
这样的代码片段就像一个禅宗的Kan:用来质疑和检验学生在修行过程中的进步的问题或语句。(译者注:禅宗可能是一种修行方式,详见维基百科)
我们将要讨论的代码片段就是这样一个例子。乍一看,它可能看起来像一个简单的字典表达式,但当你仔细想想,它会通过cpython解释器给你一个思维发展的训练。
我从这一小段代码中获得了灵感,有一次在我参加的一个Python会议上,我把它作为我演讲的内容,并以它开始我的演讲。
这也激发了我的python邮件列表成员之间的积极交流。
所以不用说,这就是代码芯片。花点时间想想下面的字典表达式,计算后会得到什么:
>。>。>。{True:'是',1:'否',1.0:'可能' }
在这里,我再等一会,大家好好想想…
5…
4…
3…
2…
1…
好的,你准备好了吗?
这是在cpython解释器的交互界面中计算上述字典表达式的结果:
>。>。>。{真:'是',1:'不是',1.0:'可能' }
{正确:“可能”}
我承认我第一次看到这个结果的时候很惊讶。但是当你逐渐研究这个过程的时候,一切都有了意义。
所以,让我们想想为什么我们会得到这个——我想说的是意想不到的——结果。
这本子词典是从哪里来的?
当python处理我们的字典表达式时,它首先构造一个新的空字典对象。然后按照字典表达式给定的顺序分配键和值。
因此,当我们分解它时,我们的字典按以下顺序表达句子:
>。>。>。xs = dict()
>。>。>。xs[True] = 'yes '
>。>。>。xs[1] = 'no '
>。>。>。xs[1.0] = '可能'
奇怪的是,Python认为这个例子中使用的所有字典键都是相等的
>。>。>。True== 1== 1.0
真正的
好的,但是在这儿等着。我肯定你能接受1.0 == 1,但事实是,为什么真也认为等于1?第一次看到这种字典式的表达,真的很困惑我。
在python文档中做了一些探索之后,我发现python使用bool作为int类型的子类。这是Python 2和Python 3的片段:
布尔类型是整数类型的一个子类型,在几乎所有上下文中,布尔值的行为分别类似于值0和1,唯一的例外是当转换为字符串时,将分别返回字符串“假”或“真”
“布尔类型是整数类型的一个子类型。在几乎所有上下文中,布尔值的行为类似于值0和1,只是当转换为字符串时,将分别返回字符串“假”或“真”。
是的,这意味着您可以在编程时使用布尔值作为Python中列表或元组的索引:
>。>。>。['否','是'][真]
“是”
但是为了可读性,不应该这样使用布尔变量。也请建议你的同事不要这样做
不管怎样,让我们回顾一下我们的字典表达式。
在python中,True、1和1.0都表示同一个字典键。当解释器计算字典表达式时,它会重复覆盖键“真”的值。这解释了为什么生成的字典只包含一个键。
在我们继续之前,让我们回顾一下最初的字典表达式:
>。>。>。{真:'是',1:'不是',1.0:'可能' }
{正确:“可能”}
为什么这里的最终结果以真为关键?由于重复赋值,最后键不应该改成1.0?
在研究了CPpython解释器源代码的一些模式后,我知道当一个新的值与字典关键字相关联时,python字典不会更新关键字对象本身:
>。>。>。ys = {1.0:'否' }
>。>。>。ys[真] = '是'
>。>。>。屈服强度
{1.0:'是' }
当然,这作为一个性能优化是有意义的——如果键被认为是相同的,为什么要花时间更新原来的呢?
在最初的例子中,您还可以看到原始的True对象从未被替换。因此,字典的字符串表示仍然以“真”作为键打印(而不是1或1.0)。
据我们所知,结果中的字典值似乎总是被覆盖,只是因为它们的键在比较后是相等的。但是,其实这个结果并不是只有__eq__比较后相等才得到的。
等等,哈希值呢?
Python字典类型由哈希表数据结构存储。当我第一次看到这个神奇的字典表达式时,我的直觉是,结果与哈希冲突有关。
哈希表中的关键字根据每个关键字的哈希值存储在不同的“桶”中。哈希值是指根据每个字典的关键字生成的固定长度的数字字符串,用于识别每个不同的关键字。
这样可以快速搜索。与其将完整的密钥对象与所有其他密钥进行比较来检查异构性,不如在哈希表中搜索与密钥相对应的哈希数字符串要快得多。
然而,哈希值的计算方法通常并不完美。而且,实际上,两个或多个不同的键会生成相同的哈希值,它们最终会出现在同一个哈希表中。
如果两个密钥的哈希值相同,则称为哈希冲突,这是在哈希表中插入和搜索元素时需要处理的特殊情况。
基于这个结论,哈希值与我们从字典表达式中得到的意外结果有很大关系。那么我们来看看这个键的哈希值在这里是否也有效。
我定义了这样一个类作为我们的测试工具:
classAlwaysEquals:
def__eq__(自我,其他):
返回真
def__hash__(self):
returnid(自我)
这个类有两个特点。
首先,因为它的__eq__ magic方法总是返回true,所以这个类和任何其他对象的所有实例都是相等的:
>。>。>。AlwaysEquals() == AlwaysEquals()
真正的
>。>。>。AlwaysEquals() == 42
真正的
>。>。>。AlwaysEquals() == 'waaat?'
真正的
其次,每个Alwaysequals实例还将返回由内置函数id()生成的唯一哈希值:
>。>。>。objects = [AlwaysEquals(),
AlwaysEquals(),
AlwaysEquals()]
>。>。>。[hash(obj)for object in objects]
[4574298968, 4574287912, 4574287072]
在CPython中,id()函数返回内存中一个对象的地址,这个地址肯定是唯一的。
有了这个类,我们现在可以创建看起来和其他对象一样的对象,但是它们都有不同的哈希值。我们可以使用它来测试字典键是否基于它们的相等比较结果而被覆盖。
如您所见,以下示例中的键不会被覆盖,即使它们总是相等的:
>。>。>。{AlwaysEquals(): 'yes ',AlwaysEquals(): 'no'}
{ & lt位于0x110a3c588 >的AlwaysEquals对象。:'是',
& lt位于0x110a3cf98 >的AlwaysEquals对象。:' no'}
现在,我们可以改变我们的想法。如果返回相同的哈希值,密钥会被覆盖吗?
classSameHash:
def__hash__(self):
返回1
SameHash类的这个实例彼此不同,但是它们具有相同的哈希值1:
>。>。>。a = SameHash()
>。>。>。b = SameHash()
>。>。>。a == b
假的
>。>。>。哈希(a),哈希(b)
(1, 1)
当我们尝试使用SameHash类的一个实例作为字典关键字时,让我们看看python字典的结果:
>。>。>。{a: 'a ',b: 'b'}
{ & lt0x7f7159020cb0 >处的SameHash实例;:' a ',
& lt0x7f7159020cf8 >处的SameHash实例;:' b'}
如本例所示,“密钥覆盖”的结果并不仅仅是由哈希冲突引起的。
嗯...那么,可以得出什么结论呢?
Python字典中的键是否相同(只有相同才会覆盖)取决于两个条件:
1,两个值是否相等(compare __eq__ method)。
2.比较它们的哈希值是否相同(compare _ _ hash _ _ method)。
让我们试着总结一下我们的研究结果:
{true:'是',1:'否',1.0:'可能' }
字典表达式的计算结果为{true:'可能' },因为键true、1和1.0都相等,并且它们都具有相同的哈希值:
>。>。>。True== 1== 1.0
真正的
>。>。>。(哈希(真)、哈希(1)、哈希(1.0))
(1, 1, 1)
`
也许不那么令人惊讶,这就是为什么我们得到这个结果作为字典的最终结果:
>。>。>。{True:'是',1:'否',1.0:'可能' }
{正确:“可能”}
我们在这里已经涵盖了许多方面,这种特殊的python技能起初可能有点不可思议——所以我在一开始将其与Zen kAn进行了比较。
如果本文内容难以理解,请尝试在Python交互环境下逐一查看代码示例。你会对python有一些深入的了解。
原文链接:https://dbader.org/blog/python-mystery-dict-expression
翻译链接:http://vimiix . com/post/2017/12/28/python-神秘学-dict-expression/
1.《疯狂字典 疯狂的Python字典》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。
2.《疯狂字典 疯狂的Python字典》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。
3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/yule/1161259.html