什么是赋值表达式(使用“海象”或“:=”运算符)?为什么要添加此语法?

2025-01-09 08:46:00
admin
原创
16
摘要:问题描述:自 Python 3.8 开始,代码可以使用PEP 572:=中记录的所谓“海象”运算符 ( )进行赋值表达式。这似乎是一个非常重要的新功能,因为它允许在理解和lambdas 中进行这种形式的分配。赋值表达式的语法、语义和语法规范到底是什么?为什么会引入这个新的(并且看似非常激进的)概念,即使PEP...

问题描述:

自 Python 3.8 开始,代码可以使用PEP 572:=中记录的所谓“海象”运算符 ( )进行赋值表达式

这似乎是一个非常重要的新功能,因为它允许在理解和lambdas 中进行这种形式的分配。

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

为什么会引入这个新的(并且看似非常激进的)概念,即使PEP 379(提出了类似“添加赋值表达式”的想法)已被撤回?


解决方案 1:

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)简化列表推导

例如:

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

可以变成:

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

语法和语义

在任何可以使用任意 Python 表达式的上下文中,都可以出现命名表达式。其形式为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)

解决方案 2:

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

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

解决方案 3:

现在 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]

解决方案 4:

什么是 := 运算符?

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

为什么需要 := 运算符?

一个简单有用的案例是减少理解中的函数调用,同时保持可读性。

让我们考虑一个列表推导,在没有运算符的情况下,如果结果大于 0,则添加一个并过滤:=。这里我们需要调用该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函数两次,这显示了运算符的优势:=

使用列表推导时要谨慎使用海象:=运算符。

以下案例可能有助于您更好地理解运算符的用法:=

案例 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

情况 3:当全局变量设置为正时

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:当全局变量设置为负数时

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

解决方案 5:

海象算子可用于避免某些函数的重新计算。以下是一个例子。

案例 1:

if complexOperation(num) > 90:
    result = complexOperation(num)
    executeActualFunctionality(result)

案例 2:

if result := complexOperation(num) > 90:
    executeActualFunctionality(result)

在案例 1 中,我们执行complexOperation()两次只是因为我们不确定结果。通过练习案例 2 可以避免这种情况。

注意:我们也可以在语句前声明结果变量if并计算complexOperation()一次。

解决方案 6:

没有海象运算符:

card_number = input("Enter card number: ")
if len(card_number) == 8:
    print(f"Card {card_number} is valid")
else:
    print(f"Card {card_number} is invalid")

使用海象运算符:

if len(card_number := input("Enter card number: ")) == 8:
    print(f"Card {card_number} is valid")
else:
    print(f"Card {card_number} is invalid")
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   984  
  在项目管理领域,CDCP(Certified Data Center Professional)认证评审是一个至关重要的环节,它不仅验证了项目团队的专业能力,还直接关系到项目的成功与否。在这一评审过程中,沟通技巧的运用至关重要。有效的沟通不仅能够确保信息的准确传递,还能增强团队协作,提升评审效率。本文将深入探讨CDCP...
华为IPD流程   0  
  IPD(Integrated Product Development,集成产品开发)是一种以客户需求为核心、跨部门协同的产品开发模式,旨在通过高效的资源整合和流程优化,提升产品开发的成功率和市场竞争力。在IPD培训课程中,掌握关键成功因素是确保团队能够有效实施这一模式的核心。以下将从五个关键成功因素展开讨论,帮助企业和...
IPD项目流程图   0  
  华为IPD(Integrated Product Development,集成产品开发)流程是华为公司在其全球化进程中逐步构建和完善的一套高效产品开发管理体系。这一流程不仅帮助华为在技术创新和产品交付上实现了质的飞跃,还为其在全球市场中赢得了显著的竞争优势。IPD的核心在于通过跨部门协作、阶段性评审和市场需求驱动,确保...
华为IPD   0  
  华为作为全球领先的通信技术解决方案提供商,其成功的背后离不开一套成熟的管理体系——集成产品开发(IPD)。IPD不仅是一种产品开发流程,更是一种系统化的管理思想,它通过跨职能团队的协作、阶段评审机制和市场需求驱动的开发模式,帮助华为在全球市场中脱颖而出。从最初的国内市场到如今的全球化布局,华为的IPD体系在多个领域展现...
IPD管理流程   0  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用