7.5 字典的键

    字典中的值没有任何限制。他们可以是任意Python对象,即从标准对象到用户自定义对象皆可。但是字典中的键是有类型限制的。

    7.5.1 不允许一个键对应多个值

    你必须明确一条原则:每个键只能对应一个项。也就是说,一键对应多个值是不允许的(像列表、元组和其他字典这样的容器对象是可以的)。当有键发生冲突(即字典键重复赋值),取最后(最近)的赋值。

    7.5 字典的键 - 图1

    7.5 字典的键 - 图2

    Python并不会因字典中的键存在冲突而产生一个错误。它不会检查键的冲突是因为,如果真这样做的话,在每个键-值对赋值的时候都会做检查,这将会占用一定量的内存。在上面的例子里,键‘foo’被列出两次,Python从左到右检查键-值对。首先值789被赋值(给键‘foo’所对应的值),然后又很快被字符串‘xyz’替代。当给字典中一个不存在的键赋值时,键和值会被创建和添加,但如果该键已经存在(键冲突),那此键所对应的值将被替换。上面例子中,键‘foo’所对应的值被替换了两次;最后的赋值语句,值123代替了值‘xyz’。

    7.5.2 键必须是可哈希的

    我们在小节7.1说过,大多数Python对象可以作为键;但它们必须是可哈希的对象。像列表和字典这样的可变类型,由于它们不是可哈希的,所以不能作为键。

    所有不可变的类型都是可哈希的,因此它们都可以作为字典的键。一个要说明的是问题是数字:值相等的数字表示相同的键。换句话来说,整型数字1和浮点型1.0的哈希值是相同的,即它们是相同的键。

    同时,也有一些可变对象(很少)是可哈希的,它们可以做字典的键,但很少见。举一个例子,一个实现了_hash_()特殊方法的类。因为_hash_()方法返回一个整型,所以仍然是用不可变的值(做字典的键)。

    为什么键必须是可哈希的?解释器调用哈希函数,根据字典中键的值来计算存储你的数据的位置。如果键是可变对象,它的值可改变。如果键发生变化,哈希函数会映像到不同的地址来存储数据。如果这样的情况发生,哈希函数就不可能可靠地存储或获取相关的数据。选择可哈希的键的原因就是因为它们的值不能改变(此问题在Python FAQ中也能找到答案)。

    我们知道数字和字符串可以被用做字典的键,但元组又怎么样呢?我们知道元组是不可变的,但在小节6.17.2,我们提示过它们也可能不是一成不变的。用元组做有效的键,必须要加限制:元组中只包括像数字和字符串这样的不可变参数,才可以作为字典中有效的键。

    我们用一个程序(userpw.py例7.1),来为本章关于字典的讲述做个小结。这个程序是用于管理用户名和密码的模拟登录数据系统。脚本接受新用户的信息:

    这个程序管理用于登录系统的用户信息:登录名字和密码。登录用户账号建立后,已存在用户可以用登录名字和密码重返系统。新用户不能用别人的登录名建立用户账号。

    例7.1

    7.5 字典的键 - 图3

    7.5 字典的键 - 图4

    例7.2 Dictionary Example (userpw.py) (continued)

    7.5 字典的键 - 图5

    他们提供登录名和密码。账号建立后,已存在用户可用登录名和正确的密码重返系统。新用户不能用别人的登录名建立账号。

    逐行解释

    1 ~ 3行

    在Unix初始行后,我们用一个空用户数据库初始化程序。因为我们没有把数据存储在任何地方,每次程序执行时都会新建一个用户数据库。

    5 ~ 15行

    newuser()函数用来建立新用户。它检查名字是否已经存在,如果证实是一个新名字,将要求用户输入他或她的密码(我们这个简单的程序没有加密),用户的密码被存储在字典里,以他们的名字做字典中的键。

    17 ~ 24行

    olduser()函数处理返回的用户。如果用户用正确的用户名和密码登录,打出欢迎信息。否则通知用户是无效登录并返回菜单。我们不会采用一个无限循环来提示用户输入正确的密码,因为用户可能会无意进入错误的菜单选项。

    26 ~ 51行

    真正控制这个脚本的是showmenu()函数,它显示给用户一个友好界面。提示信息被包括在三引号里 (”””),这样做是因为提示信息跨多行,而且比单行包含‘\n’符号的字符串更容易处理。菜单显示后,它等待用户的有效输入,然后根据菜单选项选择操作方式。try-expect语句和第6章stack.py queue.py例子里的一样(见小节6.14.1)。

    53 ~ 54行

    如果这个脚本被直接执行(不是通过import方式),这行代码会调用showmenu()函数运行程序。下面是我们的脚本运行结果。

    7.5 字典的键 - 图6