7.3 映射类型的内建函数和工厂函数

7.3.1 标准类型函数[type()、str()和 cmp()]

如你所料,对一个字典调用type()工厂方法,会返回字典类型,“<type ‘dict’>”。调用str()工厂方法将返回该字典的字符串表示形式,这些都很容易理解。

在前面的章节里,我们已经讲述了用cmp()内建函数来操作数字、字符串、列表和元组。那么字典又是如何比较的呢?字典是通过这样的算法来比较的:首先是字典的大小,然后是键,最后是值。可是,用cmp()做字典的比较一般不是很有用。

接下来的小节里,将进一步详细说明字典比较的算法,但这部分是高层次的阅读内容,可以跳过,因为字典的比较不是很有用也不常见。

*字典比较算法

接下来的例子中,我们建立两个字典进行比较,然后慢慢修改,来看看这些修改对它们之间的比较带来的影响:

7.3 映射类型的内建函数和工厂函数 - 图1

7.3 映射类型的内建函数和工厂函数 - 图2

在第一个比较中,dict1比dict2小,因为dict2有更多元素(2个vs.0个)。在向dict1添加一个元素后,dict1仍然比dict2小(2vs.1),虽然添加的元素在dict2中也存在。

7.3 映射类型的内建函数和工厂函数 - 图3

在向dict1中添加第二个元素后,两个字典的长度相同,所以用键比较大小。这时键相等,则通过它们的值比较大小。键‘host’的值相同,对于键‘port’,dict1中值比dict2中的值大(8080 vs. 80)。当把dict2中‘port’的值设成和dict1中的值一样,那么两个字典相等:它们有相同的大小、相同的键、相同的值,所以cmp()返回值是0。(本段原文中为“dict2中值比dict1中的值大”,应为笔者之误)

7.3 映射类型的内建函数和工厂函数 - 图4

当向两个字典中的任何一个添加新元素时,这个字典马上会成为大的那个字典,就像例子中的dict1一样。向dict2添加键-值对后,因为两个字典的长度又相等了,会继续比较它们的键和值。

7.3 映射类型的内建函数和工厂函数 - 图5

上面的例子表明cmp()可以返回除-1、0、1外的其他值。算法按照以下的顺序。

(1)比较字典长度

如果字典的长度不同,那么用cmp(dict1,dict2)比较大小时,如果字典dict1比dict2长,cmp()返回正值;如果dict2比dict1长,则返回负值。也就是说,字典中的键的个数越多,这个字典就越大,即

7.3 映射类型的内建函数和工厂函数 - 图6

(2)比较字典的键

如果两个字典的长度相同,那就按字典的键比较;键比较的顺序和keys()方法返回键的顺序相同。(注意:相同的键会映射到哈希表的同一位置,这保证了对字典键的检查的一致性。)这时,如果两个字典的键不匹配时,对这两个(不匹配的键)直接进行比较。当dict1中第一个不同的键大于dict2中第一个不同的键,cmp()会返回正值。

(3)比较字典的值

如果两个字典的长度相同而且它们的键也完全匹配,则用字典中每个相同的键所对应的值进行比较。一旦出现不匹配的值,就对这两个值进行直接比较。若dict1比dict2中相同的键所对应的值大,cmp()会返回正值。

(4)完全匹配

到此为止,每个字典有相同的长度、相同的键,每个键也对应相同的值,则字典完全匹配,返回0值。

图7-1说明了上述字典比较的算法

7.3 映射类型的内建函数和工厂函数 - 图7

图 7-1 字典是如何进行比较的

7.3.2 映射类型相关的函数

dict()

工厂函数被用来创建字典。如果不提供参数,会生成空字典。当容器类型对象作为一个参数传递给方法dict()时很有意思。如果参数是可以迭代的,即一个序列,或是一个迭代器,或是一个支持迭代的对象,那每个可迭代的元素必须成对出现。在每个值对中,第1个元素是字典的键,第2个元素是字典中的值。见Python文档里关于dict()的例子:

7.3 映射类型的内建函数和工厂函数 - 图8

如果输入参数是(另)一个映射对象,比如,一个字典对象,对其调用dict()会从存在的字典里复制内容来生成新的字典。新生成的字典是原来字典对象的浅复制版本,它与用字典的内建方法copy()生成的字典对象是一样的。但是从已存在的字典生成新的字典速度比用copy()方法慢,我们推荐使用copy()。

从Python2.3开始,调用dict()方法可以接受字典或关键字参数字典(函数操作符,第十一章)。

7.3 映射类型的内建函数和工厂函数 - 图9

我们提醒读者dict9的例子只作为了解dict()方法的用途,它不是现实中的例子。使用下面这些行的方法更聪明(效率更好)。

7.3 映射类型的内建函数和工厂函数 - 图10

len()

内建函数len()很灵活。它可用在序列、映像类型和集合上(在本章的后面我们会看到)。对字典调用len(),它会返回所有元素(键-值对)的数目:

7.3 映射类型的内建函数和工厂函数 - 图11

我们前面提到字典中的元素是没有顺序的。从上面的例子中可以看到,dict2的元素显示的顺序和输入时的顺序正相反。

hash()

内建函数hash()本身并不是为字典设计的方法,但它可以判断某个对象是否可以做一个字典的键。将一个对象作为参数传递给hash(),会返回这个对象的哈希值。只有这个对象是可哈希的,才可作为字典的键(函数的返回值是整型,不产生错误或异常)。

如果用比较操作符来比较两个数值,发现它们是相等的,那么即使二者的数据类型不同,它们也会得到相同的哈希值。

如果非可哈希类型作为参数传递给hash()方法,会产生TypeError错误(因此,如果使用这样的对象作为键给字典赋值时会出错):

7.3 映射类型的内建函数和工厂函数 - 图12

TypeError: dict objects are unhashable

在表7.1中,我们列出以下3个映射类型的相关函数。

7.3 映射类型的内建函数和工厂函数 - 图13