一、Python基础

1. 列表推导式与条件赋值

在生成一个数字序列的时候,在 Python 中可以如下写出:

In [1]: L = []
In [2]: def my_func(x):
   ...:     return 2*x
   ...: 
In [3]: for i in range(5):
   ...:     L.append(my_func(i))
   ...: 
In [4]: L
Out[4]: [0, 2, 4, 6, 8]

事实上可以利用列表推导式进行写法上的简化:[* for i in *] 。其中,第一个 * 为映射函数,其输入为后面 i 指代的内容,第二个 * 表示迭代的对象。

In [5]: [my_func(i) for i in range(5)]
Out[5]: [0, 2, 4, 6, 8]

列表表达式还支持多层嵌套,如下面的例子中第一个 for 为外层循环,第二个为内层循环:

In [6]: [m+'_'+n for m in ['a', 'b'] for n in ['c', 'd']]
Out[6]: ['a_c', 'a_d', 'b_c', 'b_d']

除了列表推导式,另一个实用的语法糖是带有 if 选择的条件赋值,其形式为 value = a if condition else b

In [7]: value = 'cat' if 2>1 else 'dog'
In [8]: value
Out[8]: 'cat'

等价于如下的写法:

a, b = 'cat', 'dog'
condition = 2 > 1 # 此时为True
if condition:
    value = a
else:
    value = b

下面举一个例子,截断列表中超过5的元素,即超过5的用5代替,小于5的保留原来的值:

In [9]: L = [1, 2, 3, 4, 5, 6, 7]
In [10]: [i if i <= 5 else 5 for i in L]
Out[10]: [1, 2, 3, 4, 5, 5, 5]

2. 匿名函数与map方法

有一些函数的定义具有清晰简单的映射关系,例如上面的 my_func 函数,这时候可以用匿名函数的方法简洁地表示:

In [11]: my_func = lambda x: 2*x
In [12]: my_func(3)
Out[12]: 6
In [13]: multi_para_func = lambda a, b: a + b
In [14]: multi_para_func(1, 2)
Out[14]: 3

但上面的用法其实违背了“匿名”的含义,事实上它往往在无需多处调用的场合进行使用,例如上面列表推导式中的例子,用户不关心函数的名字,只关心这种映射的关系:

In [15]: [(lambda x: 2*x)(i) for i in range(5)]
Out[15]: [0, 2, 4, 6, 8]

对于上述的这种列表推导式的匿名函数映射, Python 中提供了 map 函数来完成,它返回的是一个 map 对象,需要通过 list 转为列表:

In [16]: list(map(lambda x: 2*x, range(5)))
Out[16]: [0, 2, 4, 6, 8]

对于多个输入值的函数映射,可以通过追加迭代对象实现:

In [17]: list(map(lambda x, y: str(x)+'_'+y, range(5), list('abcde')))
Out[17]: ['0_a', '1_b', '2_c', '3_d', '4_e']

3. zip对象与enumerate方法

zip函数能够把多个可迭代对象打包成一个元组构成的可迭代对象,它返回了一个 zip 对象,通过 tuple, list 可以得到相应的打包结果:

In [18]: L1, L2, L3 = list('abc'), list('def'), list('hij')
In [19]: list(zip(L1, L2, L3))
Out[19]: [('a', 'd', 'h'), ('b', 'e', 'i'), ('c', 'f', 'j')]
In [20]: tuple(zip(L1, L2, L3))
Out[20]: (('a', 'd', 'h'), ('b', 'e', 'i'), ('c', 'f', 'j'))

往往会在循环迭代的时候使用到 zip 函数:

In [21]: for i, j, k in zip(L1, L2, L3):
   ....:     print(i, j, k)
   ....: 
a d h
b e i
c f j

enumerate 是一种特殊的打包,它可以在迭代时绑定迭代元素的遍历序号:

In [22]: L = list('abcd')
In [23]: for index, value in enumerate(L):
   ....:     print(index, value)
   ....: 
0 a
1 b
2 c
3 d

用 zip 对象也能够简单地实现这个功能:

In [24]: for index, value in zip(range(len(L)), L):
   ....:     print(index, value)
   ....: 
0 a
1 b
2 c
3 d

当需要对两个列表建立字典映射时,可以利用 zip 对象:

In [25]: dict(zip(L1, L2))
Out[25]: {'a': 'd', 'b': 'e', 'c': 'f'}

既然有了压缩函数,那么 Python 也提供了 * 操作符和 zip 联合使用来进行解压操作:

In [26]: zipped = list(zip(L1, L2, L3))
In [27]: zipped
Out[27]: [('a', 'd', 'h'), ('b', 'e', 'i'), ('c', 'f', 'j')]
In [28]: list(zip(*zipped)) # 三个元组分别对应原来的列表
Out[28]: [('a', 'b', 'c'), ('d', 'e', 'f'), ('h', 'i', 'j')]