前言
分享一个在Twitter上看到的使用海象运算符的例子,觉得挺有意思的,代码是这样的:
>>> (a := 1)
>>> (a, b := 2, 3)
>>> print(f'a={a}, b={b}')
a=1, b=2
里面这句 (a,b:=2,3)特别有迷惑性,尤其是你写过Go,很可能直觉地认为这个表达式没有问题。但是通过输出可以看到根本不符合预期,那么到底是哪里不对呢?我们慢慢地拆解一下
为什么要加圆括号
这个话题扩展起来很大,涉及到Python语法,我们逐步深入。
表达式和语句的区别
我们写的程序就是由一个或者多个语句组成的,语句(Statement)是一行或者多行代码,是整个程序的一个独立单元。而表达式(Expression)是一个特殊的语句,它只能包含标识符(字母、数字、下划线等)、字面量(Python内置常量类型,如字符串、数字、浮点数)和运算符(加减乘除、大于小于等于、异或、取余等):
1 + 2 # 表达式
a = 1 + 2 # 语句
如上面的例子, 1+2本质上是求值(等于3), a=1+2只表示代码逻辑,没有求值,只是个赋值语句
赋值表达式和赋值语句
我们在看一个例子:
In : x = 1
In : x
Out: 1
In : (y := 2)
Out: 2
In : y
Out: 2
其中 x=1是一个赋值语句(assignment statement),这样会将一个特定的值(1)设置到某个特定的存储地址去,这个位置被标记成一个特定的变量名称(x)。而 (y:=2)是一个赋值表达式(assignment expression),它比赋值语句多加了 求值这一步,也就是返回了结果(2),所以可以看到 Out:2这个输出。
而不加圆括号是语法错误:
In : y := 2
File "<ipython-input-8-b0043aac4290>", line 1
y := 2
^
SyntaxError: invalid syntax
这是因为在Python语言里,赋值表达式和赋值语句是不同的语法,下面2种方式是正确语法:
- 赋值表达式使用 :=操作符
- 赋值语句使用 =操作符
同样的,下面的代码也是语法错误:
In : (y = 2)
File "<ipython-input-13-a73f2c6719f5>", line 1
(y = 2)
^
SyntaxError: invalid syntax
所以不能直接想以 :=操作符的方式替代 =操作符,必须加一个括号来使用赋值表达式赋单个的值。
赋值语句转化成表达式的问题
刚才说赋值表达式和赋值语句语法不同,这么设计是因为将已经存在的赋值语句转化成表达式是容易出Bug的,这个在C语言里面就暴露的很明显,举个例子:
#include <stdio.h>
int main() {
int x = 3, y = 8;
if (x = y) {
printf("x and y are equal (x = %d, y = %d)", x, y);
}
return 0;
}
这是一段合规的代码,但是 (x=y)会让x被重新赋值为y的值造成这个判断为真,输出结果是 xandy are equal(x=8,y=8)。原因是代码中并没有用用于比较的操作符 ==,这种错误很隐蔽,而Python或者Go等现在编程语言都是直接明确的抛出语法错误:
In : x = 1
...: y = 2
...: if x = y:
...: print('equal')
File "<ipython-input-5-5f6807f1b35f>", line 3
if x = y:
^
SyntaxError: invalid syntax
换用正确的海象操作符是可以达到这样的逻辑的(虽然没必要):
In : x = 1
...: y = 2
...: if (x := y):
...: print(f'equal: {x=} {y=}')
...:
equal: x=2 y=2
为什么写了一句 (a:=1)
答案也就在这里,这个画蛇添足的点就是这段混乱代码的问题所在。其实很简单,试试去掉它:
In : (a, b := 2, 3)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-2-83c3cb14fd64> in <module>
----> 1 (a, b := 2, 3)
NameError: name 'a' is not defined
也就是说这句代码本身写的就不对。其实这个语句表示了一个元组,他有三个元素,分别是a, b:=2, 3。这里说a没有被定义,当然啦,之前就没赋值过a。在IPython中执行,就能理解了:
In : (a := 1) # 相当于`a = 1`
Out: 1 # 赋值a=1,并且返回这个值(1)
In : (a, b := 2, 3)
Out: (1, 2, 3)
输出其实就是就是返回输入的数据的结果,所以 (a,b:=2,3)返回值是一个元组,第一个元素a是前面赋值的结果,第二个元素是海象操作符求值的结果,第三个就是字面量3。
以上就是本次分享的所有内容,想要了解更多欢迎前往公众号:Python 编程学习圈,每日干货分享