作者: Jim Wang 公众号: 巴博萨船长

Python 标准数据类型

大家好,最近更新比较慢,临近圣诞节和新年,公司的事情也就异常多。连续几天的会议,还要加紧修复bug,为新版本软件的发布做准备。好了言归正传,书接上回。Python 定义了一些标准数据类型,用于存储各种类型的数据。Python有五个标准的数据类型,分别是:

  • Numbers(数字)
  • String(字符串)
  • List(列表)
  • Tuple(元组)
  • Dictionary(字典)

字典在Python中应该算是最后一个重要的基本变量,出现在代码中的频率也比较高。常被用作为一种可变的数据容器。今天就给大家介绍一下Python最后一个标准数据类型字典,即Dictionary的一些基础知识和在实际应用中的使用技巧。

Dictionary 字典的特征

作为一种可变容器模型,那么字典Dict在声明之后就能够任意被修改,比如添加,删除,或者更改。既然名为字典也就意味着其查询索引方式类如真实意义上的字典。Python的自己由一组键(key)与值(value)组成。两者之间用冒号隔开()。组与组之间与逗号(,)隔开。最后用花括号(大括号) “**{}**” 包裹所有的键值组,就得到一个字典实例,如下:

1
di = {key_1 : value_1, key_2 : value_2, key_3: value_3}

字典中,每一组键值,其键需要有唯一性,但是其值则不需要。在Python中,字典键值的唯一性就限制了它的数据类型选择范围。结合前面文章所介绍的的标准数据类型,除列表以外:数值型,元组,字符串都能够作为字典类型的,经过实践可以告诉大家,任何类的实例都可以作为字典的键。因为类一旦实例化,其类实例在内存中的地址具有唯一性,因此也可以作为字典的键,参考下面代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> class A:
... def __init__(self):
... pass...
>>> a = A()
>>> d = { 1: "a", 2: "b", a:"Object"}
>>> d
{<__main__.A instance at 0x0000000004D2A188>: 'Object', 1: 'a', 2: 'b'}
>>> d.keys()
[<__main__.A instance at 0x0000000004D2A188>, 1, 2]
>> d.keys()[0]
<__main__.A instance at 0x0000000004D2A188
>>>> d.keys()[0].isB = True
>>> d.keys()[0]
<__main__.A instance at 0x0000000004D2A188>

字典中,每组键值中的值,没有限定,可以是任意的数据类型。1. 键值是否可变,字典并不做约束,仅仅与数据类型本身有关。如下面代码的例子中,元组可以作为一对键值的值,因为元组本身不可变,所以该对键值的值不可变,但是不能因此认为字典的值不可改变。 2. 一对键值中,虽然字典也可以作为键值的值,而且在日常使用的时候也不会出现问题,但是在实际使用的时候,如果作为值的字典中存在bytes类的键值时,当尝试使用deepcopy复制字典实例的时候,可能会引发异常,当然类似的异常也会存在与复制相似结构的列表时诱发。各位可以留个印象,当以后遇见类似问题,或许可以帮助你确定异常诱发的原因。

1
2
3
4
5
6
7
8
>>> mydict = {"str": "String", "list": ["list1", "list2"], "tuple": (1, 2, 3) , "dictionary" : {"a": "1", "b":2}}
>>> mydict
{'list': ['list1', 'list2'], 'dictionary': {'a': '1', 'b': 2}, 'str': 'String', 'tuple': (1, 2, 3)}
>>>
注:上述代码中我们会发现一个问题,字典键值打印时,键值组的顺序和自己声明字典时的顺序
不一致。这样的问题在字典历遍或者索引的时候也会遇到。标准的Python中会有一个名为
collections的模块,在该模块下有一个名为OderedDict的类,该类为Dict的子类,但具
有保持字典中键值顺序与声明时相一致的特性。

总的来说,对于字典中键值对,键是由唯一且不可变的数据类型定义,准确地来说是可以哈希(hashable)的数据类型。值可以为任意数据类型。当同一键值被重复赋值,最后一次值将被采用。

img

声明后的字典实例,修改添加和改变都很简单。其中为字典中添加一个元素和更改一个元素的书写方式是一样的。如下方代码:如果字典情况位置,键3值的修改和键4值的添加,方法一样。而列表再添加值的时候是需要使用类方法append()的。而如果使用数字作为键值,代码文件就由如d[3] = ‘d’ 的内容, 这样的书写方法不利于代码的标准化,而且类似的需求完全可以由列表来实现。所以个人建议为了代码的标准化,使用字典时,还是应尽量避免数值类型作为键的值,避免代码出现歧义。

1
2
3
4
5
6
7
8
>>> d = { 1: "a", 2: "b", 3: "c"}
>>> d[3]
'c'
>>> d[3] = 'd'
>>> d[4] = 'e'
>>> d
{1: 'a', 2: 'b', 3: 'd', 4: 'e'}
>>>

字典的删除操作,使用字典的类方法clear()可以删除字典中的所有元素,但是字典实例依旧存在。也可以使用关键字del来实现对字典单一元素和整个字典的清除。注意:使用关键字del删除整个字典,该字典的实例将从内存中清除,使用被清除的字典会引发异常。如下面代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> d = {1: 'a', 2: 'b', 3: 'd', 4: 'e'}
>>> del d[4]
>>> d
{1: 'a', 2: 'b', 3: 'd'}
>>> d.clear()
>>> d
{}
>>> d = { 1: "a", 2: "b", 3: "c"}
>>> del d[3]
>>> d
{1: 'a', 2: 'b'}
>>> del d
>>> d
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'd' is not defined
>>>

Dictionary 字典的索引

Python中字典键值的索引总的来说有两种方法,一种使用方括号和键,类似列表的方法来索引和访问字典中的元素。第二种是使用get()类方法和键来索引和访问,代码如下:

1
2
3
4
5
>>> d = {1: 'a', 2: 'b', 3: 'd', 4: 'e'}
>>> d[1]
'a'
>> d.get(1)
'a'

两种方法在效率上并没有差别,早前读过一篇文章讨论两种索引方式的效率。结果如下,结果显示使用方括号更直接高效。个人建议大家将图示的结果仅作为一个参考,因为也有Python开发人员有不同意见,认为实验方式有问题。

在实际应用的时候,我个人觉得两种方法的效率其实并没有太大差别,更值得关注的是get()方法能够更好地避免因为键不存在引起的索引异常。如下面代码:

1
2
3
4
5
6
7
8
>>> d = {1: 'a', 2: 'b', 3: 'd', 4: 'e'}
>>> d[6]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 6
>>> d.get(6)
>>> d.get(6, "No Value")
'No Value'

由上述代码可以发现,在遇到字典中不存在的键的问题的时候,方括号+键的方式会引发异常,为了避免异常还要添加代码(通常为if语句)来检测该字典中是否存在所求键值组。而使用get()方法则能避免引发这个异常,当字典中不存在所求键值组的时,使用该方法返回空,或者你也可以给定第二个变量作为默认值,当该键值不存在则会返回默认值。在实际使用中该方法优选,可以很大程度地减少代码数量。

在这里我给大家介绍一个Python字典索引的实战经验,具体方法如下方代码。假如一个字典有数字和字符串混合键,想要得到所有数字键的键值组。经典方法为,索引字典中的每一个键值组,检查键的数据类型,将符合条件的键值组的值加入一个列表里面,最终就可以得到所有符合要求的键值组的值的组合。

1
2
3
4
5
6
7
8
9
10
11
12
>>> d = {'1': "a", 1:"b", '2': "c", 2 : "d", '3': "e", 3: "f"}
>>> d
{1: 'b', 2: 'd', 3: 'f', '1': 'a', '3': 'e', '2': 'c'}
>>> d.keys()
[1, 2, 3, '1', '3', '2']
>>> intKeys = filter(lambda e: isinstance(e, int), d.keys())
>>> intKeys
[1, 2, 3]
>>> listValuesOfIntKeys = map(d.get, intKeys)
>>> listValuesOfIntKeys
['b', 'd', 'f']
>>>

使用上述方法,可以避免索引整个字典,代码简洁有效。这里值得关注的是使用filter()+lambda关键字来筛选一个列表,和使用map()函数一次性地用不同参数调用同一函数(在这里为字典的get()方法)。这样的方法或许可以提供一种不同的解决问题的思路。

索引的特殊情况,历遍字典所有键值组。经典方法为for + in 的组合来进行迭代。请参考下方代码。

1
for key in d:

该种方法也有效,但是不推荐使用。主要原因在于,底层代码要检验键值的唯一性,该方法会增加发生异常的频率。还有一个原因是for循环中的d[key] 会导致键再次被哈希,当字典较大时,这样的操作会消耗更多的时间。

我个人比较推荐使用下面的方法,这样的方法同时迭代键与值,而且更见安全。这在Python 2.7 中常被使用:

1
for key, value in d.iteritems():

Python 3.0 会使用下面的方法:

1
for key, value in d.items():

关于字典的内置函数和类方法

字典作为Python的标准数据类型,Python这种语言自然为其准备了一些内置方法,常见的主要如下:

  1. len(dict) 函数:用于得到字典的长度,因为为键值组,所以键的数量和值的数量相当。
  2. str(dict) 函数:用于输出字典可打印的字符串,其表示内容,可通过继承字典类型, 复写repr(self)类函数来格式化输出内容。
  3. cmp(dict1, dict2)函数:用于比较字典的键值组是否完全一致,如果相同则返回真, 否则为假。

字典在Python中也算是一种类,既然是类就有类方法。上文中介绍了一些常见的类方法比如使用键得到值的get()方法,清除字典内所有元素的clear()方法和得到字典内所有键值组键的方法keys()。 除此之外常用的方法还有:

  1. dict.copy() 类方法: 用于浅复制一个字典, 注。

  2. dict.items() 类方法:返回值为该字典”(键,值)“样式的元组组成的列表 ,注。

  3. dict.update(dict2) 类方法:使用dict2中的值,来更新dict中有相同键的键

    值组的值。

  4. dict.values() 类方法:返回字典中所有值组成的列表。

  5. dict.has_key(key) 类方法:用于判断字典中是否含有该键,有为true,否则为假。

  6. dict.pop(key, default) 类方法:该方法与get()相似,删除一组键值,如果该键不

    存在则返回default值。 注: Python 2.x中有iteritems方法,在Python 3.0 中该方法被items()替代。 关于浅复制和深复制的内容,我将为在以后的文章中介绍给大家。

使用Python语言的内置方法和字典的类方法并结合一下编程技巧,我相信,各位读者能够正确使用字典来解决自己在项目中遇见的问题, 并在完成项目的同时会发现一些新的技巧,进而不断地提高自己的代码质量和代码效率。

Python基本数据类型中的字典和其使用技巧今天就介绍到这里,我也会尽我所能提高更新频率。努力归纳自己在工作中遇见的问题,并把自己找到的解决方法介绍给大家。


版权声明:
文章首发于 Jim Wang's blog , 转载文章请务必以超链接形式标明文章出处,作者信息及本版权声明。