python-3.x 什么是赋值表达式(使用“walrus”或“:=”运算符)?为什么添加此语法?

vyswwuz2  于 2023-03-31  发布在  Python
关注(0)|答案(4)|浏览(113)

从Python 3.8开始,代码可以使用所谓的“walrus”运算符(:=),在PEP 572中有记录,用于 * 赋值表达式 *。
这看起来是一个非常重要的新特性,因为它允许在解析和lambda中进行这种形式的赋值。
赋值表达式的语法、语义和语法规范究竟是什么?
为什么在PEP 379(提出了类似的“添加赋值表达式”的想法)被撤回的情况下引入了这个新的(看起来相当激进的)概念?

nxagd54h

nxagd54h1#

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)

类似地,从the docs
在本例中,赋值表达式有助于避免两次调用len():

if (n := len(a)) > 10:
    print(f"List is too long ({n} elements, expected <= 10)")
B)简化列表解析

例如:

[(lambda y: [y, x/y])(x+1) for x in range(5)]

可以变成:

[[y := x+1, x/y] for x in range(5)]

语法和语义

在任何可以使用任意Python表达式的上下文中,都可以出现一个named expression。其形式为name := expr,其中expr是任何有效的Python表达式,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)
eyh26e7m

eyh26e7m2#

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

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中还有其他很好的例子。

h43kikqp

h43kikqp3#

更多的例子和理由,现在3.8已经正式发布。
命名表达式的结果是编程的重要部分,允许使用描述性名称代替较长的表达式,并允许重用。目前,此功能仅在语句形式中可用,因此在列表解析和其他表达式上下文中不可用。
来源:LicensedProfessional's reddit comment
处理匹配的正则表达式

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

不能使用2-argiter()简单重写的循环

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]
9fkzdhlc

9fkzdhlc4#

什么是:= operator?

简单来说,:=是一个表达式+赋值运算符。它执行一个表达式,并将该表达式的结果赋值给单个变量。

为什么需要:=运算符?

一个简单有用的例子是减少解析中的函数调用,同时保持可编辑性。
让我们考虑一个列表解析,如果结果大于0,则加1并过滤,而不使用:=操作符。2这里我们需要调用add_one函数两次。

[add_one(num) for num in numbers if add_one(num) > 0]

案例1:

def add_one(num):
    return num + 1

numbers = [1,2,3,4,-2,45,6]

result1 = [value for num in numbers if (value := add_one(num)) > 0]
>>> result1
[2, 3, 4, 5, 46, 7]

结果和预期的一样,我们不需要调用add_one函数来调用两次,这显示了**:=运算符的优点
在使用列表解析时要小心使用
walarus:=运算符
下面的案例可能有助于您更好地理解
:=**运算符的用法

案例2:

def add_one(num):
    return num + 1

numbers = [1,2,3,4,-2,45,6]

>>> result2 = [(value := add_one(num)) for num in numbers if value > 0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <listcomp>
NameError: name 'value' is not defined

**第三种情况:**全局变量为正时

def add_one(num):
    return num + 1

numbers = [1,2,3,4,-2,45,6]

value = 1

result3 = [(value := add_one(num)) for num in numbers if value > 0]
>>> result3
[2, 3, 4, 5, -1]

**情况4:**全局变量设置为negitive时

def add_one(num):
    return num + 1

numbers = [1,2,3,4,-2,45,6]

value = -1

result4 = [(value := add_one(num)) for num in numbers if value > 0]
>>> result4
[]

相关问题