1. 简介
Python 3.8
引入了 PEP 572 中描述的新特性——赋值表达式,该语法由 Emily Morehouse 实现。没错,就是下面这个妹子。
2. 语法与语义
赋值表达式,这是 PEP 572
标题中使用的官方正式名称,英文是 Assignment Expressions
。也可以称作具名表达式,英文是 Named Expressions
。它涉及到的新操作符 :=
被亲切地称作海象运算符,英文是 the walrus operator
,因为冒号 :
像是海象的一对小眼睛,等号 =
像是海象的一对长牙。
赋值表达式的语法是 NAME := expr
,顾名思义,它完成了两个操作:
- 计算表达式
expr
的结果 - 把结果赋值给名为
NAME
的变量
注意:
- 在大多数
Python
表达式可以出现的上下文中,都可以使用赋值表达式。一些例外情况在PEP 572
中有所提及,刚开始使用不用在意,大体准则是不要在没有必要或者影响代码可读性的时候使用赋值表达式。 - 变量名
NAME
是有效的Python
标识符,表达式expr
可以是除了不带括号的元组以外的任意合法Python
表达式。 - 赋值表达式的值与它包含的表达式
expr
的值是一样的,同时还会把该值赋给NAME
。
3. 作用与案例
先来看一个最简单的:
>>> walrus = False
>>> print(walrus)
False
>>> print(walrus := True)
True
>>> walrus
True
可见 print(walrus := True)
一句话,做了两句话的工作:
walrus = True
print(walrus)
你想直接写成 print(walrus = True)
?在 Python
中是不行的:
>>> print(walrus = True)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'walrus' is an invalid keyword argument for print()
知道赋值表达式和海象运算符的好了吧,它可以压缩代码量,也就是压行。这下力扣上的 Python
题解又会多出很多一行解,这在以前可是做不到的。
具体来说,海象运算符经常用于给稍后需要使用的条件表达式起名字,比如 if
语句或者是 while
语句后面跟着的表达式。这么说有点不直观,再来看一个例子。
我们想要在字符串 'apple'
中从头匹配 'app'
,然后输出匹配到的内容,一般可以这么做:
>>> import re
>>> m = re.match(r'app', 'apple')
>>> if m is not None:
... print('匹配到了:', m)
...
匹配到了: <re.Match object; span=(0, 3), match='app'>
借助海象运算符,可以实现压行,改成下面这么写也没问题:
>>> import re
>>> if (m := re.match(r'app', 'apple')) is not None:
... print('匹配到了:', m)
...
匹配到了: <re.Match object; span=(0, 3), match='app'>
注意 (m := re.match(r'app', 'apple'))
两边的括号不能省略,否则等价于下面的代码:
if m := (re.match(r'app', 'apple') is not None):
...
这样得到的 m
值为 True
,就不是我们想要的 Match
对象了。
再看一个用于 while
循环的例子:
>>> while (s := input('说点什么:')) != 'q':
... print('你输入了:' + s)
...
说点什么:hello
你输入了:hello
说点什么:world
你输入了:world
说点什么:q
上面的代码不停地读取用户输入,在不为 'q'
的情况下打印输入。如果不用海象运算符,需要这么写才行:
>>> while 1:
... s = input('说点什么:')
... if s == 'q':
... break
... print('你输入了:' + s)
...
说点什么:hello
你输入了:hello
说点什么:world
你输入了:world
说点什么:q
两相比较之下,海象运算符压缩的可不仅仅是一行两行这么简单。(仔细数了数,没说错,压缩了三行。)
除此之外,海象运算符还可以复用计算开销大的值:
# Reuse a value that's expensive to compute
[y := f(x), y**2, y**3]
或者是在列表推导式的过滤子句中共享一个子表达式,这其实也算是用在 if
后面的一个例子:
# Share a subexpression between a comprehension filter clause and its output
filtered_data = [y for x in data if (y := f(x)) is not None]
更多关于海象运算符的用法和注意事项可以查看 PEP 572,切记不要为了纯粹炫技而影响代码可读性。海象虽好,可不要瞎用哦!