映射和集合 - QLGQ/learning-python GitHub Wiki
可以采用各种类型的数据作为字典的键。并非只有字符串可以作为字典的键。
字典中的键必须是可哈希的,所以数字和字符串可以作为字典中的键,但是列表和其他字典不行。
dict[1] = 'abc'
dict['1'] = 3.14159
dict[3.2] = 'xyz'
dict
{3.2: 'xyz', 1: 'abc', '1': 3.14159}
- 添加一个新数据项或新元素(即,一个键-值对)
- 修改一个已存在的数据项
- 删除一个已存在的数据项
字典中的值没有任何限制,它们可以是任意Python对象,即从标准对象到用户自定义对象皆可。但是字典中的键是有类型限制的。
每个键只能对应一个项。也就是说,一键对应多个值是不允许的(像列表、元组和其他字典这样的容器对象是可以的)。当有键发生冲突(即字典键重复赋值),取最后(最近)的赋值。
dict1 = {'foo': 789, 'foo': 'xyz'}
dict1
{'foo': 'xyz'}
dict1['foo'] = 123
dict1
{'foo': 123}
Python并不会因字典中的键存在冲突而产生一个错误。它不会检查键的冲突是因为,如果真这样做的话,在每个键-值对赋值的时候都会做检查,这将会占用一定量的内存。在上面的例子里,键'foo'被列出两次,Python从左到右检查键-值对。首先值789被赋值(给键‘foo’所对应的值),然后又很快被字符串‘xyz’替代。当给字典中一个不存在的键赋值时,键和值会被创建和添加,但如果该键已经存在(键冲突),那此键所对应的值将被替换。上面例子中,键‘foo’所对应的值被替换了两次;最后的赋值语句,值123代替了值‘xyz’。
大多数Python对象可以作为键,但它们必须是可哈希的对象。像列表和字典这样的可变类型,由于它们不是可哈希的,所以不能作为键。
**所有不可变的类型都是可哈希的,因此它们都可以作为字典的键。**一个要说明的是问题是数字:**值相等的数字表示相同的键。**换句话来说,整型数字1和浮点型1.0的哈希值是相同的,即它们是相同的键。
同时,也有一些可变对象(很少)是可哈希的,它们可以做字典的键,但很少见。举一个例子,一个实现了__hash__()特殊方法的类。因为__hash__()方法返回一个整型,所以仍然是用不可变的值(做字典的键)。
为什么键必须是可哈希的?解释器调用哈希函数,根据字典中键的值来计算存储你的数据的位置。如果键是可变对象,它的值可改变。如果键发生变化,哈希函数会映像到不同的地址来存储数据。如果这样的情况发生,哈希函数就不可能可靠地存储或获取相关的数据。选择可哈希的键的原因就是因为它们的值不能改变。
我们知道数字和字符串可以被用做字典的键,但元组又怎样呢?我们知道元组是不可变的,不过它们也可能不是一成不变的。用元组做有效的键,必须要加限制:元组中只包括像数字和字符串这样的不可变参数,才可以作为字典中有效的键。
哈希表是一种数据结构:它按照我们所要求的去工作。哈希表中存储的每一条数据,叫做一个值(value),是根据与它相关的一个被称作为键(key)的数据项进行存储的。键和值合在一起被称为“键-值对”(key-value pairs)。哈希表的算法是获取键,对键执行一个叫做哈希函数的操作,并根据计算的结果,选择在数据结构的某个地址中来存储你的值。任何一个值存储的地址皆取决于它的键。正因为这种随意性,哈希表中的值是没有顺序的。你拥有的是一个无序的数据集。
你所能获得的有序集合只能是字典中的键的集合或者值的集合。方法keys()或values()返回一个列表,该列表是可排序的。你还可以用items()方法得到包含键、值对的元组的列表来排序。由于字典本身是哈希的,所以是无序的。
**可哈希的和哈希的不是一个概念!**字典是哈希的,但不是可哈希的!
哈希表一般有很好的性能,因为用键查询相当快。Python的字典是作为可变的哈希表实现的。
函数 | 操作 |
---|---|
dict([container]) | 创建字典的工厂函数。如果提供了类容器(container),就用其中的条目填充字典,否则就创建一个空字典 |
len(mapping) | 返回映射的长度(键-值对的个数) |
hash(object) | 返回object的哈希值 |
hash()
内建函数hash()本身并不是为字典设计的,但它可以判断某个对象是否可以做一个字典的键。将一个对象作为参数传递给hash(),会返回这个对象的哈希值。只有这个对象是可哈希的,才可作为字典的键(函数的返回值是整型,不产生错误或异常)。
如果用比较操作符来比较两个数值,发现它们是相等的,那么即使二者的数据类型不同,它们也会得到相同的哈希值。
如果非可哈希类型作为参数传递给hash()方法,会产生TypeError错误(因此,如果使用这样的对象作为键给字典赋值时会出错):
hash([])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
浅拷贝: 所谓浅拷贝就是只拷贝了对对象的索引,而不是重新建立了一个对象。如果你想完全的拷贝一个对象(包括递归,如果你的对象是一个包含在容器中的容器),你需要用到深拷贝。
深拷贝:创建一个新的容器对象,包含原有对象元素(引用)全新拷贝的引用。
对一个对象进行浅拷贝其实是新创建了一个类型跟原对象一样,其内容是原来对象元素的引用,换句话说,这个拷贝的对象本身是新的,但是它的内容不是。序列类型对象的浅拷贝是默认类型拷贝,并可以以下几种方式实施(1)完全切片操
作[:];(2)利用工厂函数,比如list()、dict()等;(3)使用copy模块的copy函数。
copy模块中只有两个函数可用:copy()进行浅拷贝操作,而deepcopy进行深拷贝操作。
**集合对象是一组无序排列的可哈希的值。**也即集合成员可以做字典的键。
和其他容器类型一样,集合支持用in和not in操作符检查成员,由len()内建函数得到集合的基数(大小),用for循环迭代集合的成员。但是因为集合本身是无序的,你不可以为集合创建索引或执行切片(slice)操作,也没有键可用来获取集合中元素的值。
集合有两种不同的类型,可变集合(set)和不可变集合(frozenset)。对可变集合,可以添加和删除元素,对不可变集合则不允许这样做。
可变集合是不可哈希的,因此既不能用做字典的键也不能做其他集合中的元素。不可变集合则正好相反,也即他们有哈希值,能被用作字典的键或是作为集合中的一个成员。
可变集合是不可哈希的,但是可变集合的元素是可哈希的。
如果左右两个操作数的类型相同,即都是可变集合或不可变集合,则所产生的结果类型是相同的,但如果左右两个操作数的类型不相同(左操作数是set,右操作数是frozenset,或相反情况),则所产生的结果类型与左操作数的类型相同。
Python字典setdefaut()方法和get()方法类似,如果键还没存在于字典中,将会添加键并将值设为默认值。
setdefault()方法语法:dict.setdefault(key, default=None)
。key为查找的键值,default为键不存在时,设置的默认键值。如果键在字典中,返回这个键所对应的值。如果键不在字典中,向字典中插入这个键,并且以default为这个键的值,并返回default。default的默认值为None 。
dict = {'Name': 'Runoob', 'Age': 7}
print ("Age 键的值为 : %s" % dict.setdefault('Age', None))
print ("Sex 键的值为 : %s" % dict.setdefault('Sex', None))
print ("新字典为:", dict)
Age 键的值为 : 7
Sex 键的值为 : None
新字典为: {'Age': 7, 'Name': 'Runoob', 'Sex': None}
Python字典items()函数以列表返回可遍历的(键, 值)元组数组。
items()方法语法:dict.items()
。没有参数,返回可遍历的(键, 值)元组数组。
dict = {'Name': 'Zara', 'Age': 7}
print "Value : %s" % dict.items()
Value : [('Age', 7), ('Name', 'Zara')]
Python字典get()函数返回指定键的值,如果值不在字典中返回默认值。
get()方法语法:dict.get(key, default=None)
。key是字典中要查找的键,default为如果指定键的值不存在时,返回该默认值。该方法的返回值为指定键的值,如果值不在字典中则返回默认值。
dict = {'Name': 'Runoob', 'Age': 27}
print ("Age 值为 : %s" % dict.get('Age'))
print ("Sex 值为 : %s" % dict.get('Sex', "NA"))
Age 值为 : 27
Sex 值为 : NA