79

PEP 572引入了为 Python 3.8 实现的赋值表达式(俗称海象运算符)。这似乎是一个非常重要的新特性,因为它将允许在推导和 lambda 函数中进行这种形式的赋值。

赋值表达式的语法、语义和语法规范究竟是什么?

当PEP 379中关于“添加赋值表达式”的类似想法之前被拒绝时,为什么要引入这个新的(并且看似非常激进的概念) ?

4

3 回答 3

67

PEP 572包含许多细节,尤其是第一个问题。我将尝试简明扼要地总结/引用 PEP 的一些最重要的部分:

基本原理

允许在推导中进行这种形式的赋值,例如列表推导和禁止传统赋值的 lambda 函数。这也可以促进交互式调试,而无需重构代码。

推荐的用例示例

a) 获取条件值

例如(在 Python 3 中):

command = input("> ")
while command != "quit":
    print("You entered:", command)
    command = input("> ")

可以变成:

while (command := input("> ")) != "quit":
    print("You entered:", command)

同样,来自文档

在这个例子中,赋值表达式有助于避免调用 len() 两次:

if (n := len(a)) > 10:
    print(f"List is too long ({n} elements, expected <= 10)")

b) 简化列表推导

例如:

stuff = [(lambda y: [y,x/y])(f(x)) for x in range(5)]

可以变成:

stuff = [[y := f(x), x/y] for x in range(5)]

语法和语义

在可以使用任意 Python 表达式的任何上下文中,都可以出现命名表达式。这是任何有效的 Python 表达式的形式name := exprexpr并且 name 是一个标识符。

这种命名表达式的值与合并的表达式相同,具有额外的副作用,即为目标分配该值

与常规赋值语句的区别

除了是表达式而不是语句之外,PEP 中还提到了几个不同之处:表达式赋值从右到左,在逗号周围有不同的优先级,并且不支持:

  • 多个目标
x = y = z = 0  # Equivalent: (z := (y := (x := 0)))
  • 不分配给一个名字:
# No equivalent
a[i] = x
self.rest = []
  • 可迭代的打包/拆包
# Equivalent needs extra parentheses

loc = x, y  # Use (loc := (x, y))
info = name, phone, *rest  # Use (info := (name, phone, *rest))

# No equivalent

px, py, pz = position
name, phone, email, *other_info = contact
  • 内联类型注解:
# Closest equivalent is "p: Optional[int]" as a separate declaration
p: Optional[int] = None
  • 不支持增强分配:
total += tax  # Equivalent: (total := total + tax)
于 2018-05-11T17:50:07.597 回答
45

我最喜欢的几个例子说明了赋值表达式可以使代码更简洁、更易于阅读:

if陈述

前:

match = pattern.match(line)
if match:
    return match.group(1)

后:

if match := pattern.match(line):
    return match.group(1)

无限while陈述

前:

while True:
    data = f.read(1024)
    if not data:
        break
    use(data)

后:

while data := f.read(1024):
    use(data)

PEP 中还有其他很好的例子。

于 2019-05-22T06:41:42.060 回答
8

现在 3.8 已经正式发布,还有一些示例和基本原理。

命名表达式的结果是编程的重要部分,允许使用描述性名称代替较长的表达式,并允许重用。目前,此功能仅在语句形式中可用,使其在列表推导和其他表达式上下文中不可用。

资料来源:LicensedProfessional 的 reddit 评论

处理匹配的正则表达式

if (match := pattern.search(data)) is not None:
    # Do something with match

无法使用 2-arg iter() 重写的循环

while chunk := file.read(8192):
   process(chunk)

重用计算成本高的值

[y := f(x), y**2, y**3]

在理解过滤器子句及其输出之间共享子表达式

filtered_data = [y for x in data if (y := f(x)) is not None]
于 2019-10-15T14:14:59.710 回答