Python与字典有关的计算问题

问题

我们想在字典上对数据执行各种各样的计算(比如求最小值、最大值、排序等)。

解决方案

假设有个字典在人名和对应的工资间做了映射:

1
2
3
4
5
6
salary = {
'A': '10K',
'D': '25K',
'C': '50K',
'B': '35K'
}

为了能对字典内容做些有用的计算,通常会利用zip()将字典的键和值反转过来。例如,下面的代码会告诉我们如何找出工资最高和最低的人。

1
2
3
4
min_salary = min(zip(salary.values(), salary.keys()))
# min_salary is ('10K', 'A')
max_salary = max(zip(salary.values(), salary.keys()))
# max_salary is ('50K', 'C')

同样,要对数据排序只要使用zip()再配合sorted()就可以了,比如:

1
2
salary_sorted = sorted(zip(salary.values(), salary.keys()))
# salary_sorted is [('10K', 'A'), ('25K', 'D'), ('35K', 'B'), ('50K', 'C')]

当进行这些计算时,请注意在Python3中zip()函数创建了一个迭代器,它的内容只能被消费一次。下面的代码就是错误的:

1
2
3
salaries_and_names = zip(salary.values(), salary.keys())
print(min(salaries_and_names)) # OK
print(max(salaries_and_names)) # ValueError: max() arg is an empty sequence

在Python2中,zip()函数返回的是列表,因此不会有上述问题。

讨论

如果尝试在字典上常见的数据操作,将会发现他们只会处理键,而不是值。例如:

1
2
min(salary) # Returns A
max(salary) # Returns D

这很可能不是我们所期望的,因为实际上我们是尝试对字典的值做计算。可以利用字典的values()方法解决这个问题:

1
2
min(salary.values()) # Returns 10K
max(salary.values()) # Returns 50K

不幸的是,通常这也不是我们所期望的。比如,我们可能想知道相应的键所关联的信息是什么(哪个人的工资最高?)
如果提供一个参数key传递给min()max(),就能得到最大值和最小值所对应的键是什么。例如:

1
2
min(salary, key=lambda k: salary[k]) # Returns A
max(salary, key=lambda k: salary[k]) # Returns C

但是,要得到最小值的话,还需要额外执行一次查找。例如:

1
min_value = salary[min(salary, key=lambda k: salary[k])]

利用了zip()函数的解决方案是通过将字典的键-值对反转,为值-键对序列来解决这个问题的。
当在这样的元组上执行比较操作时,值会先进行比较,然后才是键。这完全符合我们的期望,允许我们用一条单独的语句轻松地对字典里的内容做整理和排序。
应该要注意的是,当涉及(value, key)对的比较时,如果碰巧有多个条目拥有相同的value值,那么此时key将用来作为判定结果的依据。例如,在计算min()和max()时,如果碰巧value的值相同,则将返回拥有最小或最大key值的那个条目。示例如下:

1
2
3
4
salary = {'A': '10K', 'C': '10K'}
min(zip(salary.values(), salary.keys())) # Returns ('10K', 'A')
max(zip(salary.values(), salary.keys())) # Returns ('10K', 'C')

大师兄 wechat
欢迎关注我的微信公众号:Python大师兄