前记
python中什么对象不能作为字典的key:有__hash__方法可以做字典的key,没有则不能作为字典的key;
除了list、dict、set和内部至少带有上述三种类型之一的tuple之外,其余对象均可作为字典的key;
一、list做key示例:
a = [1,2,3]
d = {a:a}报错:
TypeError: unhashable type: 'list'
报错信息,list类型不可哈希;查看源码object对象定义了__hash__方法,list、set、dict把__hash__赋值为None;
通过help(list/set/dict)也可看到__hash__赋值为None;
# 部分源码
class object:
""" The most base type """
def __hash__(self, *args, **kwargs): # real signature unknown
""" Return hash(self). """
pass
class list(object):
__hash__ = None
class set(object):
__hash__ = None
class dict(object):
__hash__ = None
二、实验
修改__hash__方法;
class Mylist(list):
def __hash__(self):
return hash(self[0])l1=Mylist([1,2])
d={l1:"can?"}
print(d)
l1.append(3)
print(d)
print(d[l1])
输出:
{[1, 2]: 'can?'}
{[1, 2, 3]: 'can?'}
can?结论:一个对象是否可以作为字典的key,取决于其有没有__hash__方法。所以python自带类型中,除了list/set/dict和内部带有以上三种类型的tuple之外,其余对象都可以作为字典的key;
三、0号元素的hash值为1,用相同hash值的对象赋值;
class Mylist(list):
def __hash__(self):
print("hash(self(0)) = ",hash(self[0]))
return hash(self[0])l1 = Mylist([1,2])
d = {}
d[l1] = l1
print(d)
d[1] = 1
print(d)
输出:
hash(self(0)) = 1
{[1, 2]: [1, 2]}
{[1, 2]: [1, 2], 1: 1}结论:没有修改而是新增一个键值对,而hash(self[0])是1,重写__eq__方法;
class Mylist(list):
def __hash__(self):
print("hash(self(0)) = ",hash(self[0]))
return hash(self[0])def __eq__(self,other):
return self[0] == otherl1 = Mylist([1,2])
d = {}
d[l1] = l1
print(d)
d[1] = 1
print(d)
输出:
{[1, 2]: [1, 2]}
{[1, 2]: 1}结论:__hash__返回值相等,且__eq__判断也相等,才认为是同一键。先后顺序?
class Mylist(list):
def __hash__(self):
print("hash(self(0)) = ",hash(self[0]))
print("hash running.")
return hash(self[0])def __eq__(self,other):
print("eq running.")
return self[0] == otherl1 = Mylist([1,2])
d = {}
d[1] = 1
print(d)
d[l1] = l1
print(d)
输出:
{1: 1}
hash(self(0)) = 1
hash running.
eq running.
{1: [1, 2]}结论:__hash__先执行,找到对应的内存,没有值直接写入;有值,判断新来的键和原来的键是否相等,相等则认为是一个键,更新值,不相等开辟空间,自增键值对。
注:如有错误,请指正!