背景
Python 的语法风格确实是懒人福音,真是做到了多写一行都是罪。就拿 Python-3.8 版本来说吧,为了少写一行代码,直接搞出了一个新的运算符 ` := `。
先给结论!这个运算符不是必要的。也就是说没有它我们也能写程序,只是有了它之后我们的代码可以更加简洁。
下面我就来说一下这个运算符的故事!
阶段 一
这个阶段大家做事都中规中矩,代码上直抒胸臆。给个例子吧。假设我们有一个函数,它是用来处理列表的,它会在处理之前检查一下列表的长度,当长度大于 7 时直接退出。阶段一这个时候的代码看起来如下。
deffun(lst=None):
"""
Parameters:
lst: 列表
Return:
None
"""
#
if len(lst) > 7:
print(f"len(lst) = {len(lst)} gt 7 , not supported .")
return
# 其它逻辑
这里有一个小小的问题, 就是 `len(lst)` 在命中 if 的语句时候它还会在 print 语句里面再被计算一次 。如果计算本身的开销就比较高,少计算一次就非常有吸引力了,阶段二就是向这个方向进化的。
阶段二
阶段二的写法也是非常直接,只要保存一下第一次计算的结果,第二次的时候直接取结果,这样就不用再计算一次了。上代码
deffun(lst=None):
"""
Parameters:
lst: 列表
Return:
None
"""
#
n = len(lst)
if n > 7:
print(f"len(lst) = {n} gt 7 , not supported .")
return
# 其它逻辑
阶段二的写法也不是完全没有可以改进的地方, 我们看 n 其实只在 if 块里面有用到,但是它的声明位置是在 if 之外的。这个就给人一种,这个 n 非常重要后面的代码还会用到它的感觉。也就是说这种写法没有办法表现出 n 就是一个临时变量 。
好在这种语义已经可以在 Python-3.8 这个版本中表达了,不过我们要借助全新的运算符 `
:=
` 来实现。 详细的请看阶段三。
阶段三
阶段三是真正的做到了形与意合,并且没有什么学习成本,语法上可以说是一看就懂,我直接上代码。
deffun(lst=None):
"""
Parameters:
lst: 列表
Return:
None
"""
#
if (n:= len(lst)) > 7:
print(f"len(lst) = {n} gt 7 , not supported .")
return
# 其它逻辑
现在从词法上看,就能非常明确地知道 n 只在 if 语句内起作用。由于阶段三只是阶段二的语法糖,也就是说从作用域上来讲 n 在 if 语句之后还是可以正常访问,这个应该就是唯一美中不足的地方了吧。
为什么说 := 是语法糖
这一点是从官方文档上不能直接看出来的,需要我们去看 fun 函数两种不同写法,编译出来的字节码。
In[1]: import dis
In[2]: def fun(lst=None):
...: """
...: Parameters:
...: lst: 列表
...:
...: Return:
...: None
...: """
...: #
...:
...: if (n:= len(lst)) > 7:
...: print(f"len(lst) = {n} gt 7 , not supported .")
...: return
...:
In[3]: dis.dis(fun)
10 RESUME 0
112 LOAD_GLOBAL 1 (NULL + len)
14LOAD_FAST 0 (lst)
16PRECALL 1
20CALL 1
30COPY 1
32STORE_FAST 1 (n)
34LOAD_CONST 1 (7)
36COMPARE_OP 4 (>)
42POP_JUMP_FORWARD_IF_FALSE 21 (to 86)
1244 LOAD_GLOBAL 3 (NULL + print)
56LOAD_CONST 2 ('len(lst) = ')
58LOAD_FAST 1 (n)
60FORMAT_VALUE 0
62LOAD_CONST 3 (' gt 7 , not supported .')
64BUILD_STRING 3
66PRECALL 1
70CALL 1
80POP_TOP
1382 LOAD_CONST 4 (None)
84RETURN_VALUE
11>> 86 LOAD_CONST 4 (None)
88RETURN_VALUE
In[4]: def fun(lst=None):
...: """
...: Parameters:
...: lst: 列表
...:
...: Return:
...: None
...: """
...: #
...: n = len(lst)
...: if n > 7:
...: print(f"len(lst) = {n} gt 7 , not supported .")
...: return
...:
In[5]: dis.dis(fun)
10 RESUME 0
102 LOAD_GLOBAL 1 (NULL + len)
14LOAD_FAST 0 (lst)
16PRECALL 1
20CALL 1
30STORE_FAST 1 (n)
1132 LOAD_FAST 1 (n)
34LOAD_CONST 1 (7)
36COMPARE_OP 4 (>)
42POP_JUMP_FORWARD_IF_FALSE 21 (to 86)
1244 LOAD_GLOBAL 3 (NULL + print)
56LOAD_CONST 2 ('len(lst) = ')
58LOAD_FAST 1 (n)
60FORMAT_VALUE 0
62LOAD_CONST 3 (' gt 7 , not supported .')
64BUILD_STRING 3
66PRECALL 1
70CALL 1
80POP_TOP
1382 LOAD_CONST 4 (None)
84RETURN_VALUE
11>> 86 LOAD_CONST 4 (None)
88RETURN_VALUE
可以看到阶段三和阶段二的字节码是一模一样的。
以上是今天的分享,最后推荐一下我的【Python潮流周刊】专栏。试运行付费模式,目前最低价,即将涨价!欢迎订阅。