我能否在Python中使用一个对象(一个类的实例)作为字典键?

14

我想把一个类的实例用作字典键,就像这样:

classinstance = class()
dictionary[classinstance] = 'hello world'

Python似乎无法处理类作为字典键,是我错了吗? 此外,我可以使用类似[(classinstance, helloworld), ...]的元组列表代替字典,但这看起来非常不专业。 你有什么方法来解决这个问题吗?


1
没有问题 - 对我来说它很好用。你为什么认为它不起作用 - 你有任何异常吗?你使用的是哪个Python版本? - Mikhail Churbanov
我使用Python 2.7 + Twisted框架。这个消息显示为: exceptions.KeyError: <__main__.xmpp instance at 0x02B2A530> - ltsstar
所以 - 我很有信心(或只是这么认为)这个异常不是在你向字典添加元素时引发的(就像你的例子中一样),而是在尝试获取它时引发的 - 在此之前检查您放入其中的内容 - 可能您用作键的实例已更改。 - Mikhail Churbanov
是的,当我想要删除它时,似乎会出现这种情况。正在检查一些细节... - ltsstar
尝试打印字典并比较实例内存地址 - 看起来你的变量和字典中的地址是不同的。 - Mikhail Churbanov
显示剩余2条评论
5个回答

15

您的实例需要是可哈希的。Python词汇表告诉我们:

如果一个对象在其生命周期内具有永远不变的哈希值(它需要一个__hash__()方法),并且可以与其他对象进行比较(它需要一个__eq__()__cmp__()方法),则该对象是可哈希的。具有相同哈希值的可哈希对象必须相等。

可哈希性使对象可用作字典键和集合成员,因为这些数据结构在内部使用哈希值。

所有Python的不可变内置对象都是可哈希的,而没有可变容器(如列表或字典)是可哈希的。默认情况下,用户定义类的实例都是可哈希的;它们都不相等,并且它们的哈希值是它们的id()。


6
这是正确的,但有误导性。默认情况下,所有对象实例都可以进行哈希处理。哈希码将是一个唯一标识该对象实例的标识符。 - mikerobi
1
它们的哈希值就是它们的ID(识别码)。这在Python3.x中已经改变,请参考https://dev59.com/5Ggu5IYBdhLWcg3wbWk9#11324771。 - gebbissimo

6
以下代码能正常工作,因为默认情况下,你的类对象是可哈希的:
Class Foo(object):
    def __init__(self):
        pass

myinstance = Foo()
mydict = {myinstance : 'Hello world'}

print mydict[myinstance]

输出: 你好,世界

此外,为了更高级的用法,您应该阅读这篇文章:

将自定义类型的对象作为字典键


5

尝试在您的类中实现 hash eq 方法。

例如,这是我制作的一个简单的可哈希字典类:

class hashable_dict:
    def __init__(self, d):
        self.my_dict = d
        self.my_frozenset = frozenset(d.items())
    def __getitem__(self, item):
        return self.my_dict[item]
    def __hash__(self):
        return hash(self.my_frozenset)
    def __eq__(self, rhs):
        return isinstance(rhs, hashable_dict) and self.my_frozenset == rhs.my_frozenset
    def __ne__(self, rhs):
       return not self == rhs
    def __str__(self):
        return 'hashable_dict(' + str(self.my_dict) + ')'
    def __repr__(self):
        return self.__str__()

3
KISS:保持简单 :-) 我认为ltsstar不需要这么复杂的代码。 - Sandro Munda
4
我不同意。我认为编写用于放入字典的类的示例非常有用。对于初学者来说,字典可能看起来像是魔法,了解对象为什么会以相同/不同的方式进行哈希非常重要,以确保您的代码按预期工作。您和我可能只是个人口味不同,但我总是喜欢例子,尤其是大约15行左右的例子。 - user

3

只要遵循规则,使用实例作为字典键是没有问题的:字典键必须是不可变的。


在编程方面,同样的问题,使用元组还是别的什么更好? - ltsstar
很难说元组更好。元组更简单。但是如果您需要将实例存储为字典键,则可能更适合您,但也更复杂。 :) - jathanism

0
你可以创建一个名为“Strategy”的文件夹,然后使用pickle保存和加载你的类的对象。
import pickle
import os

# Load object as dictionary ---------------------------------------------------
def load_object():
    file_path = 'Strategy\\All_Pickles.hd5'
    if not os.path.isfile(file_path):
        return {}
    with open(file_path, 'rb') as file:
        unpickler = pickle.Unpickler(file)
        return dict(unpickler.load())


# Save object as dictionary ---------------------------------------------------
def save_object(name, value):
    file_path = 'Strategy\\All_Pickles.hd5'
    object_dict = load_object()
    with open(file_path, 'wb') as file:
        object_dict[name] = value
        pickle.dump(object_dict, file)
        return True


class MyClass:
    def __init__(self, name):
        self.name = name

    def show(self):
        print(self.name)


save_object('1', MyClass('Test1'))
save_object('2', MyClass('Test2'))
objects = load_object()
obj1 = objects['1']
obj2 = objects['2']
obj1.show()
obj2.show()

我创建了一个类的两个对象,并调用了该类的一个方法。 希望这可以帮到你。


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