飞翔飞翔
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
  • 学习路径
  • 第1章 Python简介

    • Python是什么
    • 安装与运行
    • 交互式解释器
    • 注释与编码规范
  • 第2章 变量与数据类型

    • 变量与对象
    • 整数 int
    • 浮点数 float
    • 复数 complex
    • 布尔值 bool
    • 字符串 str
    • 空值 None
    • 类型转换
  • 第3章 运算符与表达式

    • 算术运算符
    • 比较运算符
    • 赋值运算符
    • 逻辑运算符
    • 位运算符
    • 身份与成员运算符
    • 海象运算符
    • 运算符优先级
  • 第4章 流程控制

    • if 语句
    • if-else 语句
    • if-elif-else 语句
    • match-case 语句
    • 条件表达式(三元运算符)
    • while 循环
    • for 循环
    • range 函数
    • break 与 continue
    • 循环的 else 子句
    • pass 语句
  • 第5章 数据结构

    • 列表创建与索引
    • 列表方法
    • 列表推导式
    • 元组
    • 序列解包
    • 集合
    • 字典创建与访问
    • 字典方法
    • 字典推导式
    • range 对象
  • 第6章 函数

    • 定义函数
    • 位置参数与关键字参数
    • 默认参数
    • 可变参数
    • 解包实参
    • 函数返回值
    • lambda 表达式
    • 文档字符串与注解
    • 作用域与命名空间
    • global 与 nonlocal
  • 第7章 模块与包

    • 模块导入
    • 模块搜索路径
    • 包与相对导入
    • 标准库概览
  • 第8章 文件与输入输出

    • 文件读写
    • 上下文管理器
    • 字符串格式化
    • JSON 与 CSV
  • 第9章 面向对象

    • 类与对象
    • 方法
    • 实例变量与类变量
    • 私有变量
    • 继承
    • 多重继承
    • 魔术方法
    • 属性装饰器
    • 数据类 dataclass
  • 第10章 异常处理

    • 语法错误与异常
    • try-except
    • 异常链与 raise
    • 清理操作
    • 自定义异常
  • 第11章 迭代器与生成器

    • 迭代器协议
    • 生成器
    • 生成器表达式
    • 迭代工具
  • 第12章 高级特性

    • 装饰器
    • 函数式编程
  • 第13章 工程实践

    • 测试与调试
    • 代码质量
    • 虚拟环境

海象运算符

海象运算符 :=(正式名称为"赋值表达式")在 Python 3.8 中引入,它允许在表达式内部完成赋值,将赋值和求值合并为一步。这个运算符因形似海象的獠牙而得名,解决了"先赋值再判断"场景中代码重复或需要额外变量的问题。

基本语法

海象运算符的语法是 (变量 := 表达式)。括号在大部分场景中是必需的,因为 := 的优先级较低。赋值完成后,整个表达式的值就是右侧表达式的值。

>>> (x := 5)
5
>>> x
5

与普通赋值 = 不同,海象运算符可以在不允许语句出现的表达式上下文中使用,比如 if 条件、while 条件、列表推导的过滤条件等。

while 循环场景

海象运算符最经典的用途是简化 while 循环中"读取-判断-处理"的模式。传统写法需要在循环前读取一次,循环体内再次读取,造成代码重复。

# 传统写法:input() 出现两次
line = input("请输入:")
while line != "quit":
    print(f"处理:{line}")
    line = input("请输入:")

使用海象运算符,可以将读取和判断合并:

# 海象运算符写法
while (line := input("请输入:")) != "quit":
    print(f"处理:{line}")

括号在这里是必需的,因为 := 的优先级低于 !=。如果不加括号,line := input("请输入:") != "quit" 会被解析为 line := (input(...) != "quit"),将布尔值赋给 line。

文件读取是另一个典型场景:

# 传统写法
with open("data.txt") as f:
    line = f.readline()
    while line:
        print(line.strip())
        line = f.readline()

# 海象运算符写法
with open("data.txt") as f:
    while (line := f.readline()):
        print(line.strip())

注意这里利用了一个细节:空字符串 "" 在布尔上下文中为 False,当文件读到末尾时 readline() 返回 "",循环自然结束。

if 场景

在 if 语句中,海象运算符可以在判断条件的同时保存中间结果,供 if 块和可能的 else 块复用。

# 传统写法:计算两次
if len(data) > 10:
    print(f"数据很长,长度是 {len(data)}")

# 海象运算符写法:计算一次
if (n := len(data)) > 10:
    print(f"数据很长,长度是 {n}")

正则表达式匹配是 if 场景的高频用例:

import re

# 传统写法
match = re.search(r'(\d+)', text)
if match:
    print(f"找到数字:{match.group(1)}")

# 海象运算符写法
if (match := re.search(r'(\d+)', text)):
    print(f"找到数字:{match.group(1)}")

在 elif 链中,海象运算符可以依次尝试不同的模式:

# 依次尝试不同的正则匹配
if (m := re.search(r'ERROR: (.+)', line)):
    print(f"错误:{m.group(1)}")
elif (m := re.search(r'WARN: (.+)', line)):
    print(f"警告:{m.group(1)}")
elif (m := re.search(r'INFO: (.+)', line)):
    print(f"信息:{m.group(1)}")

列表推导场景

在列表推导(以及字典推导、集合推导、生成器表达式)中,海象运算符可以避免重复计算昂贵的表达式。

data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# 传统写法:f(x) 被计算两次
results = [y for x in data if (y := x ** 2) > 20]
# 等价于:
# results = []
# for x in data:
#     y = x ** 2
#     if y > 20:
#         results.append(y)

>>> results
[25, 36, 49, 64, 81, 100]

更实用的例子是过滤并转换数据时复用计算结果:

# 获取所有文件的修改时间,只保留最近修改的
import os

files = [f for f in os.listdir('.') if (t := os.path.getmtime(f)) > threshold]
# 这里 t 被赋值但未被后续使用,更好的写法可能不需要海象运算符

一个更典型的列表推导场景是:需要同时过滤和输出基于同一计算的多个值。

# 计算平方根,只保留整数平方根
import math
numbers = [1, 2, 3, 4, 5, 9, 10, 16, 20]
perfect_squares = [n for n in numbers if (root := math.isqrt(n)) ** 2 == n]
>>> perfect_squares
[1, 4, 9, 16]

注意事项

作用域规则

海象运算符在 Python 3.8 最初引入时,如果在列表推导中使用,赋值的变量会"泄漏"到外部作用域。Python 3.12 已修复此行为,列表推导中的海象赋值现在正确限制在推导内部。

# Python 3.8-3.11(变量泄漏到外部)
>>> [y := x + 1 for x in range(3)]
[1, 2, 3]
>>> y        # y 泄漏了!
3

# Python 3.12+(变量限制在推导内)
>>> [y := x + 1 for x in range(3)]
[1, 2, 3]
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined

可读性优先

海象运算符的目的是消除代码重复,而非压缩代码行数。如果普通赋值更清晰,就不应强行使用海象运算符。

# ❌ 不要为了用而用
if (x := calculate()):
    process(x)

# ✅ 简单场景直接用普通赋值
x = calculate()
if x:
    process(x)

括号要求

海象运算符在大多数上下文中需要括号,因为它是最低优先级的运算符之一。常见的错误是遗漏括号导致语法或逻辑错误。

# 错误:优先级问题
>>> if x := 5 > 3:
...     print(x)
...
True        # x 被赋值为 (5 > 3) 即 True,不是 5

# 正确
>>> if (x := 5) > 3:
...     print(x)
...
5

不能替代所有赋值

海象运算符不能用于纯赋值语句场景——它必须作为表达式的一部分。以下写法是语法错误:

>>> (x := 5); (y := 10)        # 可以,作为表达式语句
>>> x := 5                      # 语法错误,不能作为独立语句
  File "<stdin>", line 1
    x := 5
      ^
SyntaxError: invalid syntax

海象运算符是 Python 语法演进中实用主义的体现。它在保持代码可读性的前提下,优雅地解决了"赋值+判断"模式中的重复问题。掌握其适用场景和边界,能让循环和条件代码更加紧凑而不晦涩。

上一页
身份与成员运算符
下一页
运算符优先级