Python 赋值表达式

Python 赋值表达式

赋值表达式(assignment expression)是 Python 3.8 新引入的语法,它会用到海象操作符(walrus operator)。这种写法可以解决某些持续已久的代码重复问题。a = b 是一条普通的赋值语句,读作 a equals b,而 a := b 则是赋值表达式,读作 a walrus b。

这种表达式可以在普通的赋值语句无法应用的场合实现赋值,例如可以用在条件表达式的 if 语句里面。赋值表达式的值,就是赋给海象操作符左侧那个标识符的值。

赋值表达式在 if/else 中应用

例如,如果有一筐新鲜水果要给果汁店做食材:

fresh_fruit = {
    'apple': 10,
    'banana': 8,
    'lemon': 5,
}

点柠檬汁之前,先得确认现在还有没有柠檬可以榨汁。所以,要先查出柠檬的数量,然后用 if 语句判断它是不是非零的值。

def make_lemonade(count):
    ...

def out_of_stock():
    ...

count = fresh_fruit.get('lemon', 0)
if count:
    make_lemonade(count)
else:
    out_of_stock()

这段代码看上去虽然简单,但还是显得有点儿松散,因为 count 变量虽然定义在整个 if/else 结构之上,然而只有 if 语句才会用到它,else 块根本就不需要使用这个变量。所以,这种写法让人误以为 count 是个重要的变量,if 和 else 都要用到它,但实际上并非如此。

在 Python 里面经常要先获取某个值,然后判断它是否非零,如果是就执行某段代码。对于这种用法,我们以前总是要通过各种技巧,来避免 count 这样的变量重复出现在代码之中,这些技巧有时会让代码变得比较难懂。Python 引入赋值表达式正是为了解决这样的问题。下面改用海象操作符来写:

if count := fresh_fruit.get('lemon', 0):
    make_lemonade(count)
else:
    out_of_stock()

新代码虽然只省了一行,但读起来却清晰很多,因为这种写法明确体现出 count 变量只与 if 块有关。这个赋值表达式先把 := 右边的值赋给左边的 count 变量,然后对自身求值,也就是把变量的值当成整个表达式的值。由于表达式紧跟着 if,程序会根据它的值是否非零来决定该不该执行 if 块。这种先赋值再判断的做法,正是海象操作符想要表达的意思。

柠檬汁效力强,所以只需要一颗柠檬就能做完这份订单,这意味着程序只需判断非零即可。如果客人点的是苹果汁,那就至少得用四个苹果才行。按照传统的写法,要先从 fresh_fruit 这个字典里面查出苹果(apple)的数量(count),然后在 if 语句里,根据这个数量构造条件表达式(count >=4)。

def make_cider(count):
    ...

count = fresh_fruit.get('apple', 0)
if count >= 4:
    make_cider(count)
else:
    out_of_stock() 

这段代码与刚才那个柠檬汁的例子一样,也过分突出了 count 变量的意义。下面改用海象操作符,把代码写得更清晰一些。

if (count := fresh_fruit.get('apple', 0)) >= 4:
    make_cider(count)
else:
    out_of_stock()

与刚才那个例子一样,修改之后的代码也比原来少了一行。但是这次,还要注意另外一个现象:赋值表达式本身是放在一对括号里面的。因为要在 if 语句里面把这个表达式的结果跟 4 这个值相比较。刚才柠檬汁的例子没有加括号,因为那时只凭赋值表达式本身的值就能决定 if/else 的走向:只要表达式的值不是 0,程序就进入 if 分支。但是这次不行,这次要把这个赋值表达式放在更大的表达式里面,所以必须用括号把它括起来。

还有一种类似的逻辑也会出现刚才说的重复代码,这指的是:要根据情况给某个变量赋予不同的值,紧接着要用这个变量做参数来调用某个函数。例如,若顾客要点香蕉冰沙,那首先得把香蕉切成好几份,然后用其中的两份来制作这道冰沙。如果不够两份,那就抛出香蕉不足(OutOfBananas)异常。下面用传统的写法实现这种逻辑:

def slice_bananas(count):
    ...

class OutOfBananas(Exception):
    pass

def make_smoothies(count):
    ...

pieces = 0
count = fresh_fruit.get('banana', 0)
if count >= 2:
    pieces = slice_bananas(count)

try:
    smoothies = make_smoothies(pieces)
except OutOfBananas:
    out_of_stock()

还有一种传统的写法也很常见,就是把 if/else 结构上方那条 pieces = 0 的赋值语句移动到 else 块中。

count = fresh_fruit.get('banana', 0)
if count >= 2:
    pieces = slice_bananas(count)
else:
    pieces = 0

try:
    smoothies = make_smoothies(pieces)
except OutOfBananas:
    out_of_stock()

这种写法看上去稍微有点儿怪,因为 if 与 else 这两个分支都给 pieces 变量定义了初始值。根据 Python 的作用域规则,这种分别定义变量初始值的写法是成立的。虽说成立,但这样写看起来比较别扭,所以很多人喜欢用第一种写法,也就是在进入 if/else 结构之前,先把 pieces 的初始值给设置好。

改用海象操作符来实现,可以少写一行代码,而且能够压低 count 变量的地位,让它只出现在 if 块里,这样我们就能更清楚地意识到 pieces 变量才是整段代码的重点。

if (count := fresh_fruit.get('banana', 0)) >= 2:
    pieces = slice_bananas(count)

try:
    smoothies = make_smoothies(pieces)
except OutOfBananas:
    out_of_stock()

对于在 if 与 else 分支里面分别定义 pieces 变量的写法来说,海象操作符也能让代码变得清晰,因为这次不用再把 count 变量放到整个 if/else 块的上方了。

if (count := fresh_fruit.get('banana', 0)) >= 2:
    pieces = slice_bananas(count)
else:
    pieces = 0

try:
    smoothies = make_smoothies(pieces)
except OutOfBananas:
    out_of_stock()

赋值表达式的 switch/case 模拟

Python 新手经常会遇到这样一种困难,就是找不到好办法来实现 switch/case 结构。最接近这种结构的做法是在 if/else 结构里面继续嵌套 if/else 结构,或者使用 if/elif/else 结构。例如,想按照一定的顺序自动给客人制作饮品,这样就不用点餐了。下面这段逻辑先判断能不能做香蕉冰沙,如果不能,就做苹果汁,还不行,就做柠檬汁:

count = fresh_fruit.get ('banana', 0)
if count >= 2:
    pieces = slice_bananas(count)
    to_enjoy = make_smoothies(pieces)
else:
    count = fresh_fruit.get('apple', 0)
    if count >= 4:
        to_enjoy = make_cider(count)
    else:
        count = fresh_fruit.get('lemon', 0)
        if count:
            to_enjoy = make_lemonade(count)
        else:
            to_enjoy = 'Nothing'

这种难看的写法其实在 Python 代码里特别常见。幸好现在有了海象操作符,能够轻松地模拟出很接近 switch/case 的方案。

if (count := fresh_fruit.get('banana', 0)) >= 2:
    pieces = slice_bananas(count)
    to_enjoy = make_smoothies(pieces)
elif (count := fresh_fruit.get('apple', 0)>= 4:
    to_enjoy = make_cider(count)
elif count := fresh_fruit.get('lemon'0):
    to_enjoy = make_lemonade(count)
else:
    to_enjoy = 'Nothing'

这个版本只比原来短五行,但是看起来却清晰得多,因为嵌套深度与缩进层数都变少了。只要碰到刚才那种难看的结构,就应该考虑能不能改用海象操作符来写。

当然 Python 3.10 版本引入的一种新的匹配语法,就是 match-case,是用于替代传统的 if-elif-else 结构,使代码更加简洁和易读,即 Python 版 的 switch/case。如果你还在用 Python 3.8 或者 Python 3.9 而无法使用 Python 3.10,那么在无法使用 match-case 的情况下,还是可以尝试使用赋值表达式来模拟 switch/case 以代替 if-elif-else。

最后,在其他一些场合,赋值表达式也能缩减重复代码。总之,如果某个表达式或赋值操作多次出现在一组代码里面,那就可以考虑用赋值表达式把这段代码改得简单一些。

  • 18
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值