使用不可散列的Python对象作为字典中的键

使用不可散列的Python对象作为字典中的键,python,Python,Python不允许将不可散列的对象用作其他字典中的键。正如Andrey Vlasovskikh所指出的,对于使用非嵌套字典作为键的特殊情况,有一个很好的解决方法: frozenset(a.items())#Can be put in the dictionary instead 有没有一种在字典中使用任意对象作为键的方法 示例: 如何将其用作钥匙 {"a":1, "b":{"c":10}} 在代码中实际使用类似的内容是极为罕见的。如果你认为是这样的话,首先考虑改变你的数据模型。< /强>

Python不允许将不可散列的对象用作其他字典中的键。正如Andrey Vlasovskikh所指出的,对于使用非嵌套字典作为键的特殊情况,有一个很好的解决方法:

frozenset(a.items())#Can be put in the dictionary instead
有没有一种在字典中使用任意对象作为键的方法

示例

如何将其用作钥匙

{"a":1, "b":{"c":10}}
在代码中实际使用类似的内容是极为罕见的。如果你认为是这样的话,首先考虑改变你的数据模型。< /强>

准确的用例

用例是缓存对任意只包含关键字的函数的调用。字典中的每个键都是一个字符串(参数的名称),对象可能非常复杂,由分层字典、列表、元组等组成

相关问题

这个子问题已从中分离出来。这里的解决方案处理字典不分层的情况。

使用


您可以为任何其他要使其可散列的可变类型添加其他类型测试。这应该不难。

不要。我同意Andreys对上一个问题的评论,即将字典作为键是没有意义的,尤其是不能嵌套字典。您的数据模型显然相当复杂,字典可能不是正确的答案。你应该尝试一些面向对象的方法。

如果你真的必须,让你的对象可以散列。将任何要作为键放入的子类,并提供一个_散列函数,该函数将唯一键返回给该对象

举例说明:

>>> ("a",).__hash__()
986073539
>>> {'a': 'b'}.__hash__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable
>>>(“a”),散列
986073539
>>>{'a':'b'}.\uuuuuhash\uuuuuuuu()
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
TypeError:“非类型”对象不可调用

如果您的散列不够唯一,则会发生冲突。也可能很慢。

基于Chris Lutz的解决方案。请注意,这不会处理因迭代而更改的对象,例如流,也不会处理循环

import collections

def make_hashable(obj):
    """WARNING: This function only works on a limited subset of objects
    Make a range of objects hashable. 
    Accepts embedded dictionaries, lists or tuples (including namedtuples)"""
    if isinstance(obj, collections.Hashable):
        #Fine to be hashed without any changes
        return obj
    elif isinstance(obj, collections.Mapping):
        #Convert into a frozenset instead
        items=list(obj.items())
        for i, item in enumerate(items):
                items[i]=make_hashable(item)
        return frozenset(items)
    elif isinstance(obj, collections.Iterable):
        #Convert into a tuple instead
        ret=[type(obj)]
        for i, item in enumerate(obj):
                ret.append(make_hashable(item))
        return tuple(ret)
    #Use the id of the object
    return id(obj)

我完全不同意评论和回答说,出于数据模型纯度的原因,不应该这样做

字典将一个对象与另一个对象关联,并将前者作为键。字典不能用作键,因为它们不可散列。这不会降低将词典映射到其他对象的意义/实用性/必要性

根据我对Python绑定系统的理解,您可以将任何字典绑定到多个变量(或相反,取决于您的术语),这意味着这些变量都知道指向该字典的相同唯一“指针”。难道不能使用该标识符作为散列键吗? 如果您的数据模型确保/强制执行不能将两个具有相同内容的字典用作键,那么对我来说,这似乎是一种安全的技术

我要补充的是,我完全不知道该怎么做


我不完全确定这是回答还是评论。如果需要,请更正。

我在使用基于调用签名缓存以前调用结果的装饰程序时遇到此问题。我不同意这里关于“你不应该这样做”的评论/回答,但我认为,在走这条路的时候,认识到意外和意外行为的可能性是很重要的。我的想法是,由于实例既是可变的又是可散列的,而且改变这一点似乎并不实际,因此创建非散列类型或对象的可散列等价物本质上没有什么错。当然,这只是我的观点

对于任何需要Python 2.5兼容性的人来说,下面的内容可能很有用。我是根据先前的答案得出的

from itertools import imap
tuplemap = lambda f, data: tuple(imap(f, data))
def make_hashable(obj):
  u"Returns a deep, non-destructive conversion of given object to an equivalent hashable object"
  if isinstance(obj, list):
    return tuplemap(make_hashable, iter(obj))
  elif isinstance(obj, dict):
    return frozenset(tuplemap(make_hashable, obj.iteritems()))
  elif hasattr(obj, '__hash__') and callable(obj.__hash__):
    try:
      obj.__hash__()
    except:
      if hasattr(obj, '__iter__') and callable(obj.__iter__):
        return tuplemap(make_hashable, iter(obj))
      else:
        raise NotImplementedError, 'object of type %s cannot be made hashable' % (type(obj),)
    else:
      return obj
  elif hasattr(obj, '__iter__') and callable(obj.__iter__):
    return tuplemap(make_hashable, iter(obj))
  else:
    raise NotImplementedError, 'object of type %s cannot be made hashable' % (type, obj)

再次基于Chris Lutz的解决方案

import collections

def hashable(obj):
    if isinstance(obj, collections.Hashable):
        items = obj
    elif isinstance(obj, collections.Mapping):
        items = frozenset((k, hashable(v)) for k, v in obj.iteritems())
    elif isinstance(obj, collections.Iterable):
        items = tuple(hashable(item) for item in obj)
    else:
        raise TypeError(type(obj))

    return items

我同意Lennart Regebro的观点,你没有。然而,我经常发现缓存一些函数调用、可调用对象和/或Flyweight对象很有用,因为它们可能使用关键字参数

但是如果您真的想要它,可以尝试
pickle.dumps
(或者
cPickle
如果是Python2.6)作为一种快速而肮脏的黑客攻击。它比任何使用递归调用使项不可变、字符串可散列的答案都要快得多

import pickle
hashable_str = pickle.dumps(unhashable_object)

事实上,我认为这有点复杂是对的。需要处理元组、列表、集合的特殊代码。。。实际上可能需要付出巨大的努力来妥善解决这个问题。我还应该重新命名这个问题,我认为应该是type(item)==dictalSetting item to make_hashable(item)没有在列表中设置它这是一个丑陋的黑客行为,它会分散代码的含义。如果没有人回答这个问题,那么我确实计划自己实现它(参见Chris Lutz的解决方案),并将解决方案发布在这里。然而,请随便回答,实际上这个问题相当棘手。您几乎可以保证获得类型冲突,即一种类型的dict与另一种类型的dict不同:-(仅存储类的类型不起作用,因为包含的类型可能也有类型:--1:这不是“非常罕见”。这是不必要的。使用适当的
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu哈希
函数创建不应进入对话。只需定义一个适当的类并消除此问题。我正在添加我的用例:一个类似于内存
memoize
的函数。标准
memoize
函数的问题是与db对话的开销。如果在内存中使用
memoize
,没有简单的方法可以清除r当超时不是静态的时,缓存。我不同意它应该是注释。在我看来,这是正确的答案。YMMV当然。+1:不要。如果你认为你需要它,那么你的“嵌套字典”不应该是嵌套字典--它应该是一个具有适当的
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
import pickle
hashable_str = pickle.dumps(unhashable_object)