Python中的字典进阶操作

0. 前言

在学校搞科研的时候用Python做数据分析,所以对其用法也没有深入了解,实习的时候用Python搞的开发,当时觉得没什么。但最近看了《Python Cookbook》发现自己当初写的代码太垃圾了,其中原因之一就是对字典不够熟悉、了解的过于片面。

现在我就结合《Python Cookbook》中所讲,结合自己的认识讲一讲Python中字典这一数据结构的用法。

1. 字典的定义

字典是另一种可变容器模型,且可存储任意类型对象。字典是通过名字(key)来引用值(value)的数据结构,并且把这种数据结构称为映射。直接举个简单的例子:

# 通用表达式
dic = {key1:value1, key2:value2, key3:value3}
# 举例
stu = {"ZhangSan":19, "LiSi":20, "WangWu":20}

以上的dicstu都是字典,可以发现字典是由大括号括起来的,由多个键和其对应的值构成的键值对(key-value)组成,键(key)和值(value)中间以冒号隔开。

  • 注意
    • 字典中的值没有特殊的顺序,都存储在一个特定的键(key)下;
    • 键必须是唯一的,但值则不必,键可以是数字、字符串甚至元组,值可以是任意数据类型;

2. 字典的基础操作

字典的基础操作包括:创建、增删改查,以及一些内置方法;这里不详细说,因为一是比较简单,二是如果展开说会导致本文篇幅太长。如果基础操作不懂,大家可以参考菜鸟教程文章1文章2。我想重点讲一讲出了基础操作之外的操作,这也是开发中常遇到的情况。

3. 字典中键映射多个值

假设现在有一个需求:需要创建一个字典,字典中的键值关系是一对多,那么一般有两种方法实现,一是自己创建一个空字典,然后一个个插入元素,二是利用现有的数据结构defaultdict

3.1 自己创建

自己创建有两种方法:如果数据量比较少,并且都已知,可以直接创建;如果数据量比较多,可以先创建一个空字典,然后一步步插入数据。下面分开讲。

  • 情况一:

对于字典,如果我们想要一个键映射多个值,那么就需要将这多个值放到另外的容器中,比如列表或者集合里面。可以像下面这样构造字典:

dic1 = {
    'a' : [1, 2, 3],
    'b' : [4, 5]
}
dic2 = {
    'a' : {1, 2, 3},
    'b' : {4, 5}
}

选择使用列表还是集合取决于实际需求:如果想保持元素的插入顺序就应该使用列表;如果想去掉重复元素就使用集合(并且不关心元素的顺序问题);一般不用元组作为value,因为元组是不可变的。

  • 情况二: 先创建空字典,在添加数据

如果我们自己创建一个一个多值映射字典会比较麻烦,如下代码所示,pairs中有很多映射关系,自己实现起来就比较麻烦。

pairs = {('a',1),('a',2),('a',3),('b',4),('b',5)}
d = {}
for key, value in pairs:
    if key not in d:
        d[key] = []
    d[key].append(value)
 print(d)

输出如下:
Python中的字典进阶操作

3.2 defaultdict数据结构

Python中 collections 模块中的 自带defaultdict ,我们可以使用它来构造3.1中的字典。defaultdict 的一个特征是它会自动初始化每个 key 刚开始对应的值,所以我们只需要关注添加元素操作就可以。对于3.2中的操作,我们可以改为如下:

from collections import defaultdict

pairs = {('a',1),('a',2),('a',3),('b',4),('b',5)}
dic = defaultdict(list)
for key,val in pairs:
    dic[key].append(val)
  • 注意:
    上述采用的是list装多个value,表明接受重复值,如果想去除重复值,可以把list改成set

4. 字典顺序迭代

假如我们创建一个了字典,在输出(迭代)时想按元素被插入的顺序进行输出,可 以 使 用 collections 模 块 中 的OrderedDict类,示例代码如下;

from collections import OrderedDict

d = OrderedDict()
d['foo'] = 1
d['bar'] = 2
d['spam'] = 3
d['grok'] = 4

for key in d:
    print(key, d[key])
print("字典为:", d) 

输出为:
Python中的字典进阶操作
可以看出输出的顺序和元素插入的顺序是一样的,也可以理解为“先进先出”

  • 注意:
    • OrderedDict 内部维护着一个根据键插入顺序排序的双向链表。每次当一个新的元素插入进来的时候,它会被放到链表的尾部。对于一个已经存在的键的重复赋值不会改变键的顺序;
    • 一个 OrderedDict 的大小是一个普通字典的两倍,因为它内部维护着另外一个链表。所以如果要构建一个需要大量 OrderedDict 实例的数据结构的时候(比如读取 100,000 行 CSV 数据到一个 OrderedDict 列表中去),那么就得仔细权衡一下是否使用 OrderedDict 带来的好处要大过额外内存消耗的影响。

5. 字典的运算、排序

有时候我们需要在数据字典中执行一些计算操作(比如求最小值、最大值、排序等等),那么该如何操作呢?

假设有如下字典,key表示股票名,value代表价格:

prices = {
    'ACME': 45.23,
    'AAPL': 612.78,
    'IBM': 205.55,
    'HPQ': 37.20,
    'FB': 10.75
}

5.1 情况一:我们想知道股票的最高价格和最低价格

如果直接执行操作max(prices)min(prices)得到的结果是键的“最大”和“最小”,而不是按照值进行排序。必须执行max(prices.values())min(prices.values()),这样就能得到股票的最高和最低价格,但是无法得知对应的股票名称。

5.2 情况二:我们既想知道股票的最高和最低价格,又想知道他们对应的名称

  1. 方法一:利用max()min()函数中的key

具体代码如下

max(prices.items(),key=lambda x:x[1]) # 输出为:('AAPL', 612.78)
min(prices.items(),key=lambda x:x[1]) # 输出为:('FB', 10.75)

key参数需要传递一个函数名,在进行比较前首先用这个函数对前面用来比较的每个元素进行一次预处理,也就是求max或者min的规则。后面对key传入的是一个lambda函数,表示是按照元组中第1个元素进行求最值的,所以返回是的最大value对应的元组。

在上述代码中prices.items()dict_items([('ACME', 45.23), ('AAPL', 612.78), ('IBM', 205.55), ('HPQ', 37.2), ('FB', 10.75)]),是把prices字典中的键值对转换成元组并存放在数组中。

如果还看不懂,请参考文章

  1. 方法二:利用zip()函数

可以执行以下代码。其中zip()函数是把对象中对应的元素打包成一个元组,返回这些元组组成的列表。

min_price = min(zip(prices.values(), prices.keys())) # 输出为 (10.75, 'FB')
max_price = max(zip(prices.values(), prices.keys())) # 输出为 (612.78, 'AAPL')

注意

  • zip()函数创建的是一个只能访问一次的迭代器 ,下面代码将会报错
prices_and_names = zip(prices.values(), prices.keys())
print(min(prices_and_names)) # OK
print(max(prices_and_names)) # ValueError: max() arg is an empty sequence
  • 当多个key有相同的value时,key会决定返回的结果,如下所示,两个股票价格一样,但是“AAA”<"ZZZ"所以输出如下。
prices = { 'AAA' : 45.23, 'ZZZ': 45.23 }
min(zip(prices.values(), prices.keys()))  # 输出为(45.23, 'AAA')
max(zip(prices.values(), prices.keys()))  # 输出为 (45.23, 'ZZZ')

5.3 情况三:只想知道最高和最低的股票名称

仍然可以利用在 min() 和 max() 函数中提供 key 函数参数来实现,即获取最小值或最大值对应的键的信息,具体代码如下:

min(prices, key=lambda k: prices[k]) # Returns 'FB'
max(prices, key=lambda k: prices[k]) # Returns 'AAPL

5.4 如果想按照价格对股票进行排列

  1. 方法一:利用sorted()函数中的参数key
sorted(prices.items(), key=lambda x:x[1])

输出如下:
Python中的字典进阶操作

  1. 方法二:利用zip()函数
prices_sorted = sorted(zip(prices.values(), prices.keys()))
print(prices_sorted)

输出如下:
Python中的字典进阶操作

6. 查找两个字典的相同点

假设有如下两个字典

a = {
    'x' : 1,
    'y' : 2,
    'z' : 3
} 
b = {
    'w' : 10,
    'x' : 11,
    'y' : 2
}

有如下操作可以寻找两个字典的异同点:

# 寻找a和b中相同的key
a.keys() & b.keys() # { 'x', 'y' }
# 寻找a和b中键值对相同的元组
a.items() & b.items() # { ('y', 2) }
# 寻找在a中不在b中的key,即a中有,b中没有
a.keys() - b.keys() # { 'z' }

利用以上操作也可以实现字典的过滤操作,比如以现有字典构造一个排除几个指定键的新字典,如下所示,表示过滤掉a在集合中的key。

c = {key:a[key] for key in a.keys() - {'z', 'w'}}
# c is {'y': 2, 'x': 1}
上一篇:解决phpstudy8.版本无法启动mysql服务


下一篇:JavaScript 与php phpstudy安装