Python基础:映射(字典)

1、概述

映射类型(Mapping Types)是一种关联式的容器类型,它存储了对象与对象之间的映射关系。html

字典(dict)是Python中惟一的映射类型,它是存储了一个个 键值对(由 映射到 )的关联容器。其中,(key)必须是可哈希的Python对象,而 (value)能够是任何Python对象。在功能上,Python中的字典相似于C++中的map。python

Python中最强大、最灵活的数据类型当属 列表字典,如下是对这两种数据类型的简单比较:数组

比较点 列表 字典
表示方法 [],[1, 2] {},{'a': 1, 'b': 2}
访问元素的方式 索引
有序性 有序 无序
可变性 可变 可变
可操做性 操做丰富 操做丰富
表征的数据结构 数组、堆栈、队列等 哈希表等

2、操做

字典支持的主要操做以下:markdown

操做 说明
class dict(other) 建立字典(other能够是字典、(key, value)对的迭代器或关键字参数)
dict.fromkeys(seq[, value]) 建立字典:用序列seq中的元素做为键,值全为value(未指定,则默认为None)
len(d) 返回字典d的长度(即d中元素的个数)
d[key] 若是键key在字典d中,则返回其中key对应的值;不然抛出KeyError异常
d[key] = value 设置d[key]的值为value(存在则修改,不存在则添加)
del d[key] 若是键key在字典d中,则从字典d中删除d[key];不然抛出KeyError异常
key in d 若是key在字典d中,返回True;不然,返回False
key not in d 若是key在字典d中,返回False;不然,返回True
iter(d) 同iterkeys()
d.clear() 删除字典d中的全部元素
d.copy() 返回字典d的浅拷贝
d.get(key[, default]) 若是key在字典d中,则返回d[key];不然返回default(未指定,则默认为None)
d.has_key(key) 同key in d(推荐使用key in d)
d.items() 返回包含字典d中的(key, value)对的列表
d.iteritems() 迭代版的items():返回迭代器
d.iterkeys() 迭代版的keys():返回迭代器
d.itervalues() 迭代版的values():返回迭代器
d.keys() 返回包含字典d中的键的列表
d.pop(key[, default]) 若是key在字典d中,则返回并删除d[key];不然返回default(未指定,则抛出KeyError异常)
d.popitem() 返回并删除字典d中的任意一个元素(若是d为空,则抛出KeyError异常)
d.setdefault(key[, default]) 若是key在字典d中,则返回d[key];不然执行d[key] = default,并返回default(未指定,则默认为None)
d.update([other]) 将other中的(key, value)对添加到字典d中(other能够是字典、(key, value)对的迭代器或关键字参数)
d.values() 返回包含字典d中的值的列表
d.viewitems() 返回字典d的元素视图
d.viewkeys() 返回字典d的键视图
d.viewvalues() 返回字典d的值视图

以上操做的示例以下:数据结构

>>> a = {'one': 1, 'two': 2, 'three': 3}
>>> b = dict(one=1, two=2, three=3)
>>> c = dict(zip(['one', 'two', 'three'], [1, 2, 3]))
>>> d = dict({'three': 3, 'one': 1, 'two': 2})
>>> a == b == c == d
True
>>> d = dict.fromkeys(['a', 'b', 'c'])
>>> d
{'a': None, 'c': None, 'b': None}
>>> d = dict.fromkeys(['a', 'b', 'c'], 6)
>>> d
{'a': 6, 'c': 6, 'b': 6}
>>> len(d)
3
>>> d.clear()
>>> d
{}
>>> d = a.copy()
>>> d
{'one': 1, 'three': 3, 'two': 2}
>>> d['three']
3
>>> d['four'] = 4
>>> d
{'four': 4, 'one': 1, 'three': 3, 'two': 2}
>>> del d['one']
>>> d
{'four': 4, 'three': 3, 'two': 2}
>>> 'four' in d, 'four' not in d
(True, False)
>>> d.has_key('four')
True
>>> d.get('one'), d.get('one', 10)
(None, 10)
>>> for k in d:
...     print k,
... 
four three two
>>> for k in iter(d):
...     print k,
... 
four three two
>>> for k in d.keys():
...     print k,
... 
four three two
>>> for k in d.iterkeys():
...     print k,
... 
four three two
>>> for k in d.viewkeys():
...     print k,
... 
four three two
>>> for v in d.values():
...     print v,
... 
4 3 2
>>> for v in d.itervalues():
...     print v,
... 
4 3 2
>>> for v in d.viewvalues():
...     print v,
... 
4 3 2
>>> for i in d.items():
...     print i,
... 
('four', 4) ('three', 3) ('two', 2)
>>> for i in d.iteritems():
...     print i,
... 
('four', 4) ('three', 3) ('two', 2)
>>> for i in d.viewitems():
...     print i,
... 
('four', 4) ('three', 3) ('two', 2)
>>> d.setdefault('two')
2
>>> d
{'four': 4, 'three': 3, 'two': 2}
>>> d.setdefault('one', 1)
1
>>> d
{'four': 4, 'one': 1, 'three': 3, 'two': 2}
>>> d.update(five=1)
>>> d
{'four': 4, 'one': 1, 'five': 1, 'three': 3, 'two': 2}
>>> d.update({'six': 6})
>>> d
{'four': 4, 'five': 1, 'two': 2, 'six': 6, 'three': 3, 'one': 1}
>>> d.pop('four')
4
>>> d
{'five': 1, 'two': 2, 'six': 6, 'three': 3, 'one': 1}
>>> d.popitem()
('five', 1)
>>> d
{'two': 2, 'six': 6, 'three': 3, 'one': 1}

3、字典特性

一、有序与无序

从概念上讲,字典提供了这样一种抽象:容器中的元素之间彻底独立(因而也没有前后顺序),“键”是访问元素的惟一方式。在这种 抽象层面 上,字典是 无序 的。app

从实现上讲,字典实际上是由 哈希表 实现的。而哈希表的基本思想是:经过 哈希函数(hash function)将“键”转换为“索引”,再使用“索引”去访问 连续列表(如C中的数组)中的元素。由此可知,在哈希表中:一方面,元素本质上是存储在一个连续列表中的,所以是 有序 的;另外一方面,用户没法肯定元素在连续列表中的实际位置(只能使用“键”去访问元素,而“键”与“索引”的映射关系是由哈希函数在内部指定的),所以又是 无序 的。函数

所以在 实现层面 上,字典同时具有了 无序有序 的特色:ui

  • 无序体如今:字典中元素的排列顺序与添加顺序无关
  • 有序体如今:在字典保持不变的状况下,字典中元素的排列顺序是固定的

字典的哈希表实现

上图对应的示例以下:3d

# 无序
>>> d = {}
>>> d['a'] = 1
>>> d
{'a': 1}
>>> d['b'] = 2
>>> d
{'a': 1, 'b': 2}
>>> d['c'] = 3
>>> d
{'a': 1, 'c': 3, 'b': 2}

# 有序
>>> for k in d: # 键的顺序固定
...     print k,
... 
a c b
>>> for v in d.values(): # 值的顺序固定
...     print v,
... 
1 3 2
>>> for i in d.items(): # 元素的顺序固定
...     print i,
... 
('a', 1) ('c', 3) ('b', 2)

二、字典的键

字典的键具备如下特性:code

1)可哈希的(hashable

只有 可哈希的 对象才能做为字典的键,一个可哈希的对象必须知足如下两个条件:

  • 该对象在其生命周期内有一个不变的哈希值(须要实现__hash__()方法)
  • 该对象是可比较的(须要实现__eq__()__cmp__()方法)

Python中可哈希的对象有:

  • 数值、字符串,以及只含有数值或字符串的元组
  • 用户自定义类的实例(默认是可哈希的,也能够经过实现__hash__()__cmp__()来修改默认行为)

2)哈希等价键

假设有字典d的两个键:keyA和keyB,咱们称keyA和keyB是 哈希等价键(本身杜撰的名词),若是keyA和keyB知足如下两个条件:

  • hash(keyA) == hash(keyB)
  • cmp(keyA, keyB) == 0

若是keyA和keyB是哈希等价键,那么它们将被视为彻底相同的两个键,因而d[keyA]和d[keyB]会指向同一个字典元素。

例如,1和1.0就知足上述两个条件,所以是哈希等价键:

>>> hash(1), hash(1.0)
(1, 1)
>>> cmp(1, 1.0)
0
>>> d = {}
>>> d[1] = 'int 1'
>>> d
{1: 'int 1'}
>>> d[1.0] = 'float 1'
>>> d
{1: 'float 1'}

对于用户自定义的类实例,默认状况下(即没有实现__hash__()__cmp__()时),hash(...)和cmp(...)的结果与 id() 有关(参考 hashable__cmp__())。默认状况下,一个自定义类的任意两个实例都不是哈希等价键:

>>> class A: pass
... 
>>> a1 = A()
>>> a2 = A()
>>> hash(a1), hash(a2)
(-1064359592, -1064359600)
>>> cmp(a1, a2)
1
>>> d = {}
>>> d[a1] = 'a1'
>>> d
{<__main__.A instance at 0x8f2958c>: 'a1'}
>>> d[a2] = 'a2'
>>> d
{<__main__.A instance at 0x8f2958c>: 'a1', <__main__.A instance at 0x8f2950c>: 'a2'}

若是想要让同一个类的任意两个实例都是哈希等价键,则能够参考如下示例:

>>> class A:
...     def __hash__(self):
...         return hash(A)
...     def __cmp__(self, other):
...         return cmp(self.__hash__(), other.__hash__())
... 
>>> a1 = A()
>>> a2 = A()
>>> hash(a1), hash(a2)
(-1064346499, -1064346499)
>>> cmp(a1, a2)
0
>>> d = {}
>>> d[a1] = 'a1'
>>> d
{<__main__.A instance at 0x8f64a4c>: 'a1'}
>>> d[a2] = 'a2'
>>> d
{<__main__.A instance at 0x8f64a4c>: 'a2'}

相似地,若是想要让一个类的任意一个实例与整数1成为哈希等价键,则能够按照如下方式实现:

>>> class A:
...     def __hash__(self):
...         return 1
...     def __cmp__(self, other):
...         return cmp(self.__hash__(), other.__hash__())
... 
>>> a = A()
>>> hash(1), hash(a)
(1, 1)
>>> cmp(1, a)
0
>>> d = {}
>>> d[1] = 'int 1'
>>> d
{1: 'int 1'}
>>> d[a] = 'instance a'
>>> d
{1: 'instance a'}

4、字典视图

从2.7版本开始,Python中引入了字典视图(Dictionary views)。字典视图 是字典的 动态视图:它们会与字典保持同步,实时反应出字典的变化。字典视图共有3种:键视图(Keys views)、值视图(Values views)和 元素视图(Items views),它们分别由dict.viewkeys()、dict.viewvalues()和dict.viewitems()三个函数返回。

全部的字典视图都支持如下操做:

操做 说明
len(dictview) 返回字典的长度
iter(dictview) 返回(键、值、元素)迭代器
x in dictview 判断x是否为(键、值、元素)的成员

此外,由于字典的键是 惟一可哈希的,因此 键视图 还支持 相似集合(set-like)的操做。若是字典的值是 可哈希的,那么 元素视图 也支持这些操做:

操做 说明
dictview & other 求交集
dictview | other 求并集
dictview - other 求差集
dictview ^ other 求对称差集

关于字典视图的示例,请参考 Dictionary view objects

5、应用

一、模拟switch-case语句

如下是C中一个使用switch-case语句的示例:

int select(char c)
{
    int num = -1;

    switch (c)
    {
        case 'a':
            num = 1;
        break;

        case 'b':
            num = 2;
        break;

        case 'c':
            num = 3;
        break;

        default:
            num = 0;
        break;
    }

    return num;
}

Python中没有提供switch-case语句,但使用字典能够轻松实现相似上面的功能:

d = {'a': 1, 'b': 2, 'c': 3}

# 普通版本
def select1(c):
    num = -1

    if c not in d:
        num = 0
    else:
        num = d[c]

    return num

# 惊呆版本
def select2(c):
    return d.get(c, 0)

二、稀疏矩阵

使用元组做为字典的键,能够构建相似稀疏矩阵的数据结构:

>>> matrix = {}
>>> matrix[(2, 3, 4)] = 88
>>> matrix[(7, 8, 9)] = 99
>>> 
>>> matrix
{(2, 3, 4): 88, (7, 8, 9): 99}
>>> 
>>> x, y, z = 2, 3, 4
>>> matrix[(x, y, z)]
88

三、符号表

实际上,Python自己就在内部大量使用了字典,一个典型的应用就是符号表:

>>> locals() # 局部符号表 
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', '__doc__': None, '__package__': None}
>>> globals() # 全局符号表 
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', '__doc__': None, '__package__': None}
相关文章
相关标签/搜索