Python-tricks

收集于 Dan Bader 博客

一行代码合并字典(dict)

实际开发中,经常需要合并字典,例如:default={'name':'Tom','age':18,'gender':'man'} 默认配置,用户的配置 user={'name':'Jak','age':20},这时就需要合并两个字典,获取总的配置。

假设有两个字典(dict): x 和 y ,合并成一个新的 dict ,不改变 x 和 y 的值:

1
2
>>> x = {'a': 1, 'b': 2}
>>> y = {'b': 3, 'c': 4}

期待得到一个新的结果 z ,如果 key 相同,则 y 会覆盖 x :

1
2
>>> z
{'a': 1, 'b': 3, 'c': 4}

python3.5+

在 PEP448 中,有个新语法可以实现,并且在 python3.5中支持该语法:

1
2
3
>>> z = {**x,**y}
>>> z
{'a': 1, 'b': 3, 'c': 4}

一行代码搞定

这种语法还支持一次合并多个字典(三个及三个以上):

1
2
3
>>> u = {'c':5,'d':6}
>>> {**x,**y,**u}
{'a': 1, 'b': 3, 'c': 5, 'd': 6}

python2.x

对于 python2.x 和 python3.0-3.4 的版本,有比较优雅的合并,但需要两行代码:

1
2
>>> z = x.copy()
>>> z.update(y)

当然,还有个一行代码合并的方法:

1
2
3
>>> z = dict(x,**y)
>>> z
{'a': 1, 'b': 3, 'c': 4}

一次测试多个标记的不同方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 一次测试多个标记
x, y, z = 0, 1, 0
if x == 1 or y == 1 or z == 1:
print('passed')
if 1 in (x, y, z):
print('passed')
# 只测试是否为真
if x or y or z:
print('passed')
if any((x, y, z)):
print('passed')

对字典按key排序和按value排序

字典类型是无序的,因而对字典排序的原理是,将字典转变为列表或者元组。

对字典排序,需要借助函数 sorted(),这是python内置的一个排序函数,它会从一个迭代器返回一个排好序的新列表。

sorted(iterable,key,reverse) 共有三个参数。
iterable 表示可迭代的对象,如 dict.items( )、dict.key( )等。
key 是一个函数,用来选取参与排序的元素。reverse 用来指定排序是倒序还是顺序。true是倒序,false是顺序,默认 reverse=false

按value排序

借助 lambda 表达式选取参与排序的元素:

1
2
3
>>> xs = {'a': 4, 'b': 3, 'c': 2, 'd': 1}
>>> sorted(xs.items(), key=lambda x: x[1])
[('d', 1), ('c', 2), ('b', 3), ('a', 4)]

items() : 将字典转为可迭代对象,会将字典元素转为元组
lambda x: x[1] : 选取元组中第二个元素作为排序参数并输出

或者可以借助 operator 模块的 itemgetter( ) 函数,该函数用于获取对象的哪些维的数据,参数为一些序号(即需要获取的数据在对象中的序号):

1
2
3
>>> import operator
>>> sorted(xs.items(), key=operator.itemgetter(1))
[('d', 1), ('c', 2), ('b', 3), ('a', 4)]

注意,operator.itemgetter 函数获取的不是值,而是定义了一个函数,通过该函数作用到对象上才能获取值。

按key排序

1
2
3
4
5
6
7
>>> xs = {'c': 4, 'a': 3, 'd': 2, 'b': 1}
>>> sorted(xs.items(),key=lambda x:x[0])
[('a', 3), ('b', 1), ('c', 4), ('d', 2)]
>>> sorted(xs.keys())
['a', 'b', 'c', 'd']

sorted函数的key参数选默认时,字典默认是按 key 排序

1
2
>>> sorted(xs.items())
[('a', 3), ('b', 1), ('c', 4), ('d', 2)]

巧用dict的get()方法返回默认值

python 的 dict 自带有 get(key) 方法,根据指定的key,返回对应的value

想象下,我们有个字典结构的数据,user ID 为 key,user name 为 value。

1
2
3
4
5
name_for_userid = {
382: "Alice",
950: "Bob",
590: "Dilbert",
}

现在想写一个函数 greeting() ,可以根据 user ID 返回对应的 user name 。

第一个版本如下:

1
2
def greeting(userid):
return "Hi %s!" % name_for_userid[userid]

然而当输入非法的key时,会抛出 exception。这样非常不好,会直接导致运行中断。

1
2
3
4
5
6
7
>>> greeting(382)
'Hi Alice!'
>>> greeting(23333)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in greeting
KeyError: 23333

所以,我们需要考虑输入的 user ID 找不到的情况,可以设置个默认返回值。

第二个版本如下:

1
2
3
4
5
6
7
8
9
10
11
def greeting(userid):
if userid in name_for_userid:
return "Hi %s!" % name_for_userid[userid]
else:
return "Hi there!"
>>> greeting(382)
"Hi Alice!"
>>> greeting(23333)
"Hi there!"

然而这种方法很不好:

  • 这是低效的,因为查询了两次字典。
  • 代码冗长的,不简洁。
  • 没有[pythonic]的味道。

因而,一个更好的方法是用 try ... except 语句来捕捉并处理异常。

第三个版本如下:

1
2
3
4
5
def greeting(userid):
try:
return "Hi %s!" % name_for_userid[userid]
except KeyError:
return "Hi there"

以上方法的处理,虽然正确,但仍然不够简洁。python 的 dict 自带 get() 方法,支持设置一个默认返回值。

第四个版本:

1
2
3
4
5
6
7
8
def greeting(userid):
return "Hi %s!" % name_for_userid.get(userid, "there")
>>> greeting(950)
"Hi Bob!"
>>> greeting(23333)
"Hi there!"

相比这四种方法,最后一种更加的简洁、干净、只靠使用pyhton标准库的特性就能处理完成。

更方便的两个变量换值的方法

通常我们编程,都是通过借用一个临时变量来交换两个变量的值:

1
2
3
4
5
>>> a = 23
>>> b = 56
>>> tmp = a
>>> a = b
>>> b = tmp

但在 Python 中,有更方便、更简洁的方法:

1
>>> a,b = b,a