2020 年的最后一周写一点轻松的东西,熟练使用这些技巧会给日常工作减负不少:本文中会提到几个使用一行代码可以完成简单任务的方法。
替代 if
语句:Ternary Operator
标准用法
先来看一个简单的 if
语句:
1 | if 5 > 3: |
怎么用一行代码完成同样的任务呢?有请 Ternary Operator:
1 | True_expression if True_condition else False_expression |
意思是这样的:判断 True_condition
是否成立,如果成立,执行 True_expression
;如果不成立,执行 False_expression
。那么上面的 if
语句可以写成
1 | '5 is more than 3' if 5 > 3 else '5 is less than 3' |
Ternary Operator 的变体
- Shorthanded Ternary如果
1
expression or another_expression
expression
为True
,那么another_expression
则不会执行;反之则会执行。比如:因为1
2
3
4True or 'something'
# True
False or 'something'
#'something'None
等于False
,这可以用于检查一个函数是否有输出:也可以用来设定一个函数的动态变量:1
2
3output = None
msg = output or 'No data returned'
# 'No data returned'1
2
3
4
5def display_name(name, default=None):
displayeded_name = dafault or name
print(displayed_name)
display_name('Jim') # 'Jim'
display_name('Tom', 'anonymous123') # 'anonymous123' - 这个变体同样巧妙地应用了
False == 0
和True == 1
这两个性质。这里1
(expr_if_False, expr_if_true)[True_or_False]
(expr_if_False, expr_if_true)
用了 tuple,但是也可以用 list。也可以用bool()
函数将所有变量转换为0
或1
,因为False == 0
,当True_or_False
为假时会取第一个元素;当True_or_False
为真时会取第二个元素。1
2
3output = 'blabla'
['Not have an output', 'Has an output'][bool(output)]
# 'Has an output'替代
再来看一个简单的自定义函数:def
语句:lambda
匿名函数这个简单的函数可以使用1
2
3def add(x, y):
return x + y
add(1, 2) # 3lambda
匿名函数完成。lambda
匿名函数必须在一行内完成,语法为:那么上面的两数相加的函数也可以写成:1
lambda arguments: expression
1
2add = lambda x, y: x + y
add(1, 2) # 3一行代码生成一个容器:解析式
基础列表解析式
假如我们希望写一个函数将一个列表中的数字平方在返回一个新列表:这么一个简单的函数居然要用 5 行代码,尴尬症都要犯了。解析式来救场!解析式又分列表解析式、集合解析式和字典解析式,以列表解析式为例,语法为:1
2
3
4
5def square(lst):
new_lst = []
for num in lst:
new_lst.append(num**2)
return new_lst所以我们可以将上面的函数改写为:1
[expression for var in iterable if condition]
一行搞定!要注意的是,只有当元素符合判定条件(1
[var**2 for var in lst]
if
)时才会被处理,解析式里没有else
。嵌套列表解析式
更复杂的列表解析式包含多个变量:我们应该怎么写呢?根据解析式的 PEP202 文档,1
[expression for var1 in iterable1 if condition1 for var2 in iterable2 if condition2]
It is proposed to allow conditional construction of list literals using for and if clauses. They would nest in the same way for loops and if statements nest now.
如果我们想写一个 for
循环来做同样的事情,我们可能这样写:
1 | for var1 in iterable1: |
使用解析式,我们只需要将正常写法最后的表达式写在最前面,之后依次把嵌套循环的代码依次写在同一行就行了。我们来看一个终极例子:
有一个嵌套单词列表,如果单词有至少两个字母,则返回一个包含所有字母与它所在单词的列表的索引的新列表。
如果我们用常规方法,可能会这么写:
1 | new_lst = [] |
使用列表解析式可以这么写:
1 | strings = [ ['foo', 'bar'], ['baz', 'taz'], ['w', 'koko'] ] |
集合解析式与字典解析式
集合解析式与列表解析式差不多,区别仅仅是将 []
换成了 {}
。字典解析式与列表解析式的区别在于将 var
换成了键值对key, val
。比如在 NLP 应用中,需要生成一个词与索引的 lookup 字典:
1 | id2word = {idx:word for (idx, word) in enumerate(corpus)} |
替代 yield
语句:生成器
yield
语句是一种一边循环一边计算的机制,将一个函数中的 return
替换成 yield
,使用 next
调用该函数:
1 | def generator(x): |
也可以使用一行代码将一个简单的 yield
语句实现,这就是生成器。生成器与列表解析式的语法一样,只是用 ()
代替了 []
。
1 | x = (i for i in range(5)) |
本文中的技巧仅可用于一些简单的任务,如果任务比较复杂,这些技巧要么无法完成,要么可读性大幅下降而变得难以理解与维护。