Python 3.8 新运算符 := 让我们懒出新高度

2023-03-24 14:35:51 浏览数 (1)


背景

Python 的语法风格确实是懒人福音,真是做到了多写一行都是罪。就拿 Python-3.8 版本来说吧,为了少写一行代码,直接搞出了一个新的运算符 `:= `。

先给结论!这个运算符不是必要的。也就是说没有它我们也能写程序,只是有了它之后我们的代码可以更加简洁。

下面我就来说一下这个运算符的故事!


阶段 一

这个阶段大家做事都中规中矩,代码上直抒胸臆。给个例子吧。假设我们有一个函数,它是用来处理列表的,它会在处理之前检查一下列表的长度,当长度大于 7 时直接退出。阶段一这个时候的代码看起来如下。

代码语言:javascript复制
def fun(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 语句里面再被计算一次。如果计算本身的开销就比较高,少计算一次就非常有吸引力了,阶段二就是向这个方向进化的。


阶段二

阶段二的写法也是非常直接,只要保存一下第一次计算的结果,第二次的时候直接取结果,这样就不用再计算一次了。上代码

代码语言:javascript复制
def fun(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 这个版本中表达了,不过我们要借助全新的运算符 `:=` 来实现。 详细的请看阶段三。


阶段三

阶段三是真正的做到了形与意合,并且没有什么学习成本,语法上可以说是一看就懂,我直接上代码。

代码语言:javascript复制
def fun(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 函数两种不同写法,编译出来的字节码。

代码语言:javascript复制
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)
  1           0 RESUME                   0

 11           2 LOAD_GLOBAL              1 (NULL   len)
             14 LOAD_FAST                0 (lst)
             16 PRECALL                  1
             20 CALL                     1
             30 COPY                     1
             32 STORE_FAST               1 (n)
             34 LOAD_CONST               1 (7)
             36 COMPARE_OP               4 (>)
             42 POP_JUMP_FORWARD_IF_FALSE    21 (to 86)

 12          44 LOAD_GLOBAL              3 (NULL   print)
             56 LOAD_CONST               2 ('len(lst) = ')
             58 LOAD_FAST                1 (n)
             60 FORMAT_VALUE             0
             62 LOAD_CONST               3 ('  gt 7 , not supported .')
             64 BUILD_STRING             3
             66 PRECALL                  1
             70 CALL                     1
             80 POP_TOP

 13          82 LOAD_CONST               4 (None)
             84 RETURN_VALUE

 11     >>   86 LOAD_CONST               4 (None)
             88 RETURN_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)
  1           0 RESUME                   0

 10           2 LOAD_GLOBAL              1 (NULL   len)
             14 LOAD_FAST                0 (lst)
             16 PRECALL                  1
             20 CALL                     1
             30 STORE_FAST               1 (n)

 11          32 LOAD_FAST                1 (n)
             34 LOAD_CONST               1 (7)
             36 COMPARE_OP               4 (>)
             42 POP_JUMP_FORWARD_IF_FALSE    21 (to 86)

 12          44 LOAD_GLOBAL              3 (NULL   print)
             56 LOAD_CONST               2 ('len(lst) = ')
             58 LOAD_FAST                1 (n)
             60 FORMAT_VALUE             0
             62 LOAD_CONST               3 ('  gt 7 , not supported .')
             64 BUILD_STRING             3
             66 PRECALL                  1
             70 CALL                     1
             80 POP_TOP

 13          82 LOAD_CONST               4 (None)
             84 RETURN_VALUE

 11     >>   86 LOAD_CONST               4 (None)
             88 RETURN_VALUE

可以看到阶段三和阶段二的字节码是一模一样的。


最后

都到这里了,是时候图穷匕见了!

0 人点赞