python的标准字典类dict和set
由于字典和集合都是程序中经常需要使用的数据结构,python语言的内置类型包含一个字典类型(dict)和两个集合类型(set和frozenset)。
在python语言的官方实现中,字典和两个集合类型都是基于散列表技术实现的数据结构,采用内消解技术解决冲突。
以dict为例:
- dict类型采用散列表技术实现,其元素是key-value(键-值)对,键可以是任何不可变对象,值可以是任何对象。
- 在创建空字典或很小的字典时,初始分配的存储区可容纳8个元素。
- dict对象在负载因子超过2/3时自动更换更大的存储区,并把已经保存的内容重新散列到新存储区。如果当前字典对象不太大,就按当时字典中实际元素的4倍分配新的存储区。当字典中的元素超过50000时,改为按实际元素个数的两倍分配新存储区。
上面说的字典dict,其实python中集合的情况类似,许多代码实现完全一致。当然frozenset是不可变对象,一旦建立不会动态变化。
字典等类型除了作为提供给编程人员使用的数据结构外,在解释器系统的实现也很有用。例如,在python系统,不少内部机制的实现也基于字典结构,如全局/模块/类的名字空间等。在程序运行中,需要找到各种名字的关联信息。在一个名字空间中,特别时全局的活大型的模块里,可能定义了很多名字,用字典实现效率更高。
python规定dict的键,以及set和frozenset的元素都只能是不可变对象,是为保证散列表的完整性(为保证数据项检索和删除的正确实现)。这里的原因也很清楚:如果允许键是可变对象,插入元素时根据键计算的位置,在键发生变动后就不符合散列检索的要求了,随后就无法正确找到他们。如果需要正确修改数据的键,只能是先删除数据项,然后用新的键重新插入数据。
在python便准函数中有一个hash函数,其功能是按特定的方式计算参数的散列值。对一个对象调用hash函数,他或者返回一个整数,或者抛出一个异常,表示本函数对该对象无定义。hash函数无定义的函数有list列表对象,或包含可变成分的序对对象,调用时将抛出异常TypeError: unhashable type。在dict、set等类型的实现中,都用这个hash函数计算键(或元素)的散列值。
对python的各种内置的不可变类型,hash函数都有定义,包括内置的不可变组合类型,如str、tuple、frozenset等。这个函数的定义还保证,当a==b成立时两个对象的hash值也相等。
python在这里采用了与其他机制一样的设计:程序调用标准函数hash时,解释器将到参数所属的类里去找名为__hash__的方法。在python的内置类型中,标准函数hash有定义的类型都有自己的__hash__方法,没有__hash__方法即是hash函数无定义。
如果希望自定义类的对象也能作为dict或set等的键,就应该为这个类定义一个__hash__方法。该类的对象是否可变时用户自己的事情。如果对象可变,加入字典或集合后修改这种对象的值,