Python字典没有分配所有的键或项。

27

我创建了以下字典

exDict = {True: 0, False: 1, 1: 'a', 2: 'b'}

当我打印出exDict.keys()时,它返回一个生成器。好的,我把它转换为列表,然后它就给了我


[False, True, 2]

为什么没有1?当我打印exDict.items()时,它给了我


[(False, 1), (True, 'a'), (2, 'b')]

有人猜测这里发生了什么事吗?我很困惑。


2
不错的难题!Python的设计者们尽力避免惊喜,但是并不能完全避免... - alexis
3
你的物品中已经有了解决方案:(True, 'a'),这是 1 的值。 - njzk2
4个回答

29

这种情况发生是因为True == 1(同样地,False == 0,但你没有将0作为键)。你需要进行代码或数据的重构,因为dict在判断键是否相同时会考虑它们是否“相等”(而不是is)。


1
我不确定是否有确定性。无论如何,我也不会依赖它。 - John Zwinck
1
@heemayl 即使字典中项目的顺序没有保证,我认为它使用的键将是首次遇到的键。当它找到一个等效的现有键时,它不会替换该键。 - Mark Ransom
1
与Mark Ransom所建议的相反,在当前2.7.x和3.x中,最终值是等效关键字的最后一个遇到的值(在你的例子中是a)。这也是有意义的,如果我们考虑到字典可能是迭代地从文字构建的,因此"稍后"的映射将覆盖"早期"的映射。 - mikołak
5
希望在字典字面量中出现相同的键会引发运行时错误。 - jpmc26
1
@mikołak,我说的不是,而是。我刚刚测试了Python 2.7.10,它按照我说的方式工作,保留原始键。它也按照你说的方式工作,保留最后一个值。 - Mark Ransom
显示剩余4条评论

12
你所看到的是Python将1强制转换为True。你会看到你打印的字典是:
False  1
True   a
2      b

本来想将值 a 分配给 1,但实际上将 a 重新分配为 True 的值。

根据Python 3 文档

布尔类型是整数类型的子类型,在几乎所有情况下,布尔值的行为与值 0 和 1 相同,唯一的例外是在转换为字符串时,分别返回字符串 "False" 或 "True"。

强调是我的。

注意:在 python 2.X 中,可以重新分配 TrueFalse,因此无法保证这种行为。


5

Python将1视为True。布尔类型是整数类型的子类型。

In [1]: a = {}

In [2]: a[True] = 0

In [3]: 1 in a.keys()
Out[3]: True

2
如果您在Python的字典(dict)中插入一个键值对,Python会检查该键是否已经存在,如果存在,它将替换当前的值。
这个检查大致是这样的:
def hash_and_value_equal(key1, key2):
    return hash(key1) == hash(key2) and key1 == key2

所以不仅值必须相等,而且它们的hash也必须相等。不幸的是,True1,以及False0都将被视为相等的键:

>>> hash_and_value_equal(0, False)
True

>>> hash_and_value_equal(1, True)
True

因此,它们替换了值(但不是键):

>>> a = {1: 0}
>>> a[True] = 2
>>> a
{1: 2}

>>> a = {False: 0}
>>> a[0] = 2
>>> a
{False: 2}

我展示了手动添加键的情况,但是使用 dict字面值 时采取的步骤是相同的:

>>> a = {False: 0, 0: 2}
>>> a
{False: 2}

或者使用内置的 dict
>>> a = dict(((0, 0), (False, 2)))
>>> a
{0: 2}

如果您编写自己的类并希望将它们用作字典中的潜在键,那么这可能非常重要。根据您对__eq____hash__的实现,它们将或不将替换相等但不完全相同的键的值:

class IntContainer(object):
    def __init__(self, value):
        self.value = value

    def __eq__(self, other):
        return self.value == other

    def __hash__(self):
        # Just offsetting the hash is enough because it also checks equality
        return hash(1 + self.value)

>>> hash_equal(1, IntContainer(1))
False

>>> hash_equal(2, IntContainer(1))
False

所以这些不会替换现有的整数键:
>>> a = {1: 2, IntContainer(1): 3, 2: 4}
>>> a
{1: 2, <__main__.IntContainer at 0x1ee1258fe80>: 3, 2: 4}

或者被视为相同键的内容:
class AnotherIntContainer(IntContainer):
    def __hash__(self):
        # Not offsetted hash (collides with integer)
        return hash(self.value)

>>> hash_and_value_equal(1, AnotherIntContainer(1))
True

现在这些将替换整数键:
>>> a = {1: 2, AnotherIntContainer(1): 5}
>>> a
{1: 5}

唯一非常重要的是要记住,如果对象和它们的哈希值相等,则字典键被视为相等。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接