“:=”语法和赋值表达式:什么和为什么?

2024-06-16 08:37:03 发布

您现在位置:Python中文网/ 问答频道 /正文

PEP 572引入了为Python 3.8实现的赋值表达式(俗称为Walrus运算符)。这看起来是一个非常重要的新特性,因为它允许在理解和lambda函数中使用这种形式的赋值。

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

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


Tags: lambda函数规范概念表达式语义语法运算符
3条回答

PEP 572包含许多细节,尤其是第一个问题。我将尽量简明地总结/引用政治公众人物最重要的部分:

基本原理

允许在理解(如列表理解)和lambda函数中使用这种形式的赋值,而传统的赋值是被禁止的。这也可以促进交互调试,而无需进行代码重构。

推荐的用例示例

a)获取条件值

例如(在Python 3中):

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

可以变成:

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

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)]

语法和语义

In any context where arbitrary Python expressions can be used, a named expression can appear. This is of the form name := expr where expr is any valid Python expression, and name is an identifier.

The value of such a named expression is the same as the incorporated expression, with the additional side-effect that the target is assigned that value

与常规赋值语句的区别

除了是表达式而不是语句之外,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)

下面是我最喜欢的几个例子,其中赋值表达式可以使代码更简洁、更易于阅读:

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)

other good examples in the PEP

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

Naming the result of an expression is an important part of programming, allowing a descriptive name to be used in place of a longer expression, and permitting reuse. Currently, this feature is available only in statement form, making it unavailable in list comprehensions and other expression contexts.

来源:LicensedProfessional's reddit comment

Handle a matched regex

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

A loop that can't be trivially rewritten using 2-arg iter()

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

Reuse a value that's expensive to compute

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

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]

相关问题 更多 >