如何使用字符串列表[可能包含任何字符]作为键?

时间:2014-07-02 09:48:42

标签: python list dictionary stringification

基本上我“理解”使用其他容器作为键的歧义。 - 您是比较参考还是比较其中的值(以及深度)。

那么你不能只专门化一个列表并制作一个自定义比较/哈希运算符吗?正如我在我的应用程序中所知,我希望通过字符串的值(以及当然的相对顺序)来比较字符串列表。

那么我该如何为这类列表编写自定义哈希呢?或者换句话说 - 如何在不导致引入分隔符的情况下对字符串进行字符串化(分隔符可能是字符串的一部分)?!

关于这个问题:https://wiki.python.org/moin/DictionaryKeys
直接说不能使用列表;

  
    

也就是说,为什么列表不能用作字典键的简单答案是列表不提供有效的哈希方法。当然,显而易见的问题是,“为什么不呢?”

  

所以我写这个问题是否有办法使列表可以清除;以及如何制作出令人满意的 Hash 方法。


作为我想要的原因示例,请参阅以下代码:

namelist = sorted(["Jan", "Frank", "Kim"])
commonTrait = newTraits()
traitdict = {}
traitdict[namelist] = commonTrait;

//later I wish to retrieve it:
trait = traitdict[sorted(["Jan", "Frank", "Kim"])]

在这个直接使用中我记得“列表的排序”并不重要(排序只是在上面的代码中完成,以使列表总是相等,如果它们保持相同的内容)。

1 个答案:

答案 0 :(得分:11)

如果您需要使用字符串集合作为字典键,您有两个明显的选择:如果订单很重要,请使用tuple

>>> d[tuple(['foo', 'bar'])] = 'baz'
>>> d['foo', 'bar']
baz
>>> d['bar', 'foo']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: ('bar', 'foo')

如果订单不能重要,请使用frozenset

>>> d[frozenset(['foo', 'bar'])] = 'baz'
>>> d[frozenset(['bar', 'foo'])]
'baz'
>>> d[frozenset(['bar', 'foo', 'bar'])]
'baz'

如果计数很重要但排序不重要,请将sortedtuple一起使用:

>>> d[tuple(sorted(['foo', 'bar']))] = 'baz'
>>> d[tuple(sorted(['bar', 'foo']))]
'baz'
>>> d[tuple(sorted(['bar', 'foo', 'bar']))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: ('bar', 'bar', 'foo')

与Perl哈希或JavaScript对象属性不同,您不需要在Python中对字典键进行字符串化。


现在,关于可变的list不可清除:Python字典实现使用哈希表结构。它特别要求并假设关键:

  1. 该键实现了返回整数的__hash__方法
  2. 整数应该在输出范围内广泛传播,并尽可能均匀映射。
  3. __hash__方法为同一对象返回不变的数字
  4. a == b表示a.__hash__() == b.__hash__()
  5. 列表不能用作字典键,如:

    >>> [].__hash__()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: 'NoneType' object is not callable
    

    list课程无法提供同时满足所有要求的__hash__方法a == b需要暗示a.__hash__() == b.__hash__()

    (它*可以提供一个为每个列表返回0的实现,然后它会正常工作,但它会完全反驳哈希的使用,因为所有列表都将映射到字典中的相同插槽,如哈希码会破坏规则2)。

    也无法为列表创建__hash__方法:

    >>> [].__hash__ = lambda x: 0
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'list' object attribute '__hash__' is read-only
    

    当然,如果list拥有__hash__方法,我们总能看到会发生什么 - 我们创建了列表的子类并在那里提供__hash__;哈希码的明显实现是tuple()

    的实现
    >>> class hashablelist(list):
    ...     def __hash__(self):
    ...         return hash(tuple(self))
    ... 
    >>> x = hashablelist(['a', 'b', 'c'])
    >>> y = hashablelist(['a', 'b', 'd'])
    >>> d = {}
    >>> d[x] = 'foo'
    >>> d[y] = 'bar'
    >>> d.items()
    [(['a', 'b', 'c'], 'foo'), (['a', 'b', 'd'], 'bar')]
    >>> y[2] = 'c'
    >>> d.items()
    [(['a', 'b', 'c'], 'foo'), (['a', 'b', 'c'], 'bar')]
    >>> del d[x]
    >>> del d[y]
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    KeyError: ['a', 'b', 'c']
    >>> d.items()
    [(['a', 'b', 'c'], 'bar')]
    >>> x in d
    False
    >>> y in d
    False
    >>> x in d.keys()
    True
    >>> y in d.keys()
    True
    

    代码显示我们刚刚设法获取了一个损坏的字典 - 即使在['a', 'b', 'c'] -> 'bar'中可见,也无法通过密钥直接访问或删除.keys()对。 ,.values().items()