IPython,可变的dict键,可能的精神错乱 [英] IPython, mutable dict keys, possible insanity

查看:64
本文介绍了IPython,可变的dict键,可能的精神错乱的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Dict键可能应该是不可变的,但这并不是实际上的要求.所需要做的就是可以对密钥进行哈希处理.如果我以将对象的哈希值插入字典后更改其哈希值的方式更改对象,会发生什么情况?除了一般的坏事"之外,我还有一些意想不到的行为.

Dict-keys should probably be immutable, but that's not actually a requirement. All that's required is that the key can be hashed. What happens if I change the object in such a way that its hash changes after inserting it into my dict? Besides just generally "bad things", I'm getting some extra-unexpected behavior.

>>> class Foo(object):
        def __init__(self, n):
            self.n = n
        def __hash__(self):
            return self.n

>>> foo = Foo(1)
>>> d = {foo : foo.n}
>>> print(d)
{<__main__.Foo at 0xdeadbeef: 1}
>>> d
{<__main__.Foo at 0xdeadbeef: 1}

到目前为止,太好了.遵循规则的时间已经过去.让我们做些愚蠢的事情:

so far, so good. The time for following rules is over. Let's do something dumb:

>>> foo.n += 1

现在,我正在运行IPython 5.1.0(通过anaconda安装),该计算机正在运行Python 3.5.2(在Mac上?不确定哪个系统详细信息很有趣-索取更多信息,我会很高兴的添加).

Now, I'm running IPython 5.1.0 (installed via anaconda), which is running Python 3.5.2 (on a Mac? not sure yet which system details are interesting - ask for more info and I'll be happy to add it).

>>> print(d)
{<__main__.Foo at 0xdeadbeef: 1}
>>> d
KeyError ...
IPython/core/formatters.py
    print.pretty(obj)
IPython/lib/pretty.py
    return self.type_pprinters[cls](obj, self, cycle)
IPython/lib/pretty.py
    p.pretty(obj[key])
KeyError: <__main__.Foo at 0xdeadbeef>

这让我感到惊讶/困惑-如果我们可以正确地print该对象,为什么IPython无法弄清楚如何print它呢?似乎正在尝试查找密钥,由于哈希值已更改,因此当然找不到该密钥,但是-为什么print(d)可以正常工作?

This is surprising/confusing to me - if we can correctly print the object, why can't IPython figure out how to print it? It seems like it's trying to look up the key, which of course it can't find because the hash changed, but - why then does print(d) work just fine?

好吧,还没傻呢:

>>> d[foo] = foo.n

从逻辑上讲-foo的哈希值已更改,因此它不会认识到它已拥有此键"-还没有已经具有此键.并且:

Logically thinking - the hash of foo changed so it'll not recognize that it "already has this key" - it doesn't already have this key. And:

>>> print(d)
{__main__.Foo at 0xdeadbeef: 1, __main__.Foo at 0xdeadbeef: 2}

但是随后,要求IPython显示:

>>> d
{__main__.Foo at 0xdeadbeef: 2, __main__.Foo at 0xdeadbeef: 2}

在指针和底线之间可能很难看清,但是它认为字典中的 BOTH 值都是2.基于上面的stacktrace,我猜这是因为它试图使用它所引用的foo,而不是实际上在我们的字典键中查看 at ,而是...想到了它知道...?并仅使用引用(对于我们当前的foofoo.n=2,它知道"值是foo.n而不是常规整数")?这可能是最令人困惑的部分,在此我将不胜感激.

Might be a little hard to see amidst the pointers and dunderscores, but it thinks BOTH values in our dictionary are 2. Based on the stacktrace above, I'm guessing this is because it tries to use the foo it has a reference to, and instead of actually looking at our dictionary keys, it ...thinks it knows...? And just uses the reference (for our current foo, with foo.n=2, and it "knows" the values are foo.n not "regular integers")? This is probably the most baffling part, and where I would appreciate some understanding.

最后一个问题:这是IPython中的错误(在这种情况下,我将尝试提交错误报告)还是使用可哈希但可变的dict键并将其更改并将其重新添加到字典中的过程Python中的未定义行为"?就print(d)的输出而言,它似乎定义得很好,但也许我遗漏了一些东西.

Final question: is this a bug in IPython (in which case I'll try to file a bug-report) or is the process of using hashable-but-mutable dict keys and changing them and re-adding them to dictionaries "undefined behavior" in Python? It seems pretty well-defined in terms of output from print(d), but maybe I'm missing something.

推荐答案

为了完整起见,我将在此处给出一个答案,以便我们关闭此循环:

For the sake of completeness, I'll put an answer here so we can close this loop:

我的问题的开头说

"Dict键可能应该是不可变的,但这实际上不是 要求.所需要的就是密钥可以被散列."

"Dict-keys should probably be immutable, but that's not actually a requirement. All that's required is that the key can be hashed."

但是,正如@Stefan Pochmann指出的那样,这还不够精确. dict键必须是可散列的,并且散列在对象的整个生命周期中都不得更改.

But, as pointed out by @Stefan Pochmann, that's not precise enough to be accurate. The dict-key must be hashable and the hash should never change over the object's lifetime.

这并不意味着 object 必须是不可变的,但这确实意味着馈入哈希的对象部分不应以改变输出的方式改变__hash__()调用.

That doesn't mean the object must be immutable, but it does mean the parts of the object that feed into the hash shouldn't change in such a way that they'd change the output of the __hash__() call.

因此,当我以更改哈希的方式更改实例时,我违反了要求,所有赌注都消失了-我无法再期望该对象的行为了.然后,使用IPython的问题"就只是对我违反的对象做出了有效的假设(这些假设,而不是对象),因此对我犯错是完全可以理解的.

So, when I altered my instance in such a way as to change the hash, I violated the requirement and all bets are off - I can no longer make expectations about how this object will act. The "issue" with IPython then is just a matter of it making valid assumptions about objects that I've violated (the assumptions, not the objects), so it's perfectly understandable for it to error out on me.

这篇关于IPython,可变的dict键,可能的精神错乱的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆