Python ipaddress.collapse_addresses 怎样合并连续网段
来源:17golang原创
时间:2026-11-01 10:04:44 334浏览 收藏
如果一组网段来自配置文件、访问控制列表或地址池,直接把它们原样输出,往往会留下重复项和可以继续合并的相邻块。Python 标准库的 ipaddress.collapse_addresses() 可以把同一 IP 版本的网络对象交给 CIDR 规则统一折叠,返回更紧凑的网段迭代器。
最小用法是先用ip_network()把输入转换为网络对象,再调用collapse_addresses()。IPv4 和 IPv6 要分批处理;函数只会合并满足前缀对齐条件的网络,不会因为两个网段“看起来相邻”就强行扩大范围。
官方文档:https://docs.python.org/3/library/ipaddress.html#ipaddress.collapse_addresses
先分清它合并的是什么
collapse_addresses() 处理的是 IPv4Network 或 IPv6Network 对象组成的可迭代对象,不是任意字符串列表。它返回一个迭代器,里面仍然是网络对象。
“连续网段”也有一个容易忽略的限制:两个网络不仅要覆盖连续的地址范围,还要能组成一个合法的更大 CIDR 网络。例如 192.0.2.0/25 和 192.0.2.128/25 正好组成 192.0.2.0/24;如果边界没有按更大前缀对齐,函数就会保留为多个结果。

最小写法:输入转换后直接折叠
下面的例子把字符串解析成网络对象,再把返回迭代器转换为列表。注释只标出输入类型、惰性返回值和最终展示这几个关键点。
from ipaddress import collapse_addresses, ip_network
def collapse_cidrs(values: list[str]) -> list[str]:
# 先把文本统一解析为网络对象,strict=False 会屏蔽误写的主机位
networks = [ip_network(value, strict=False) for value in values]
# 函数返回迭代器;物化后更适合交给接口或配置渲染层
collapsed = collapse_addresses(networks)
# 用 str() 输出规范化后的 CIDR 文本
return [str(network) for network in collapsed]
values = [
"192.0.2.128/25",
"192.0.2.0/25",
"192.0.2.0/25", # 重复输入不会让结果重复扩大
]
print(collapse_cidrs(values))
['192.0.2.0/24']
输入顺序不影响这个示例的折叠结果,重复的 /25 也不会把地址范围扩展到 192.0.3.0/24。如果业务还需要保留原始输入顺序,应在合并前另行保存原始列表。
为什么有些相邻网段仍然不能合并
合并的目标必须是一个合法的更大网络。下面的函数把几个边界样例放在一起,便于观察“可合并”“仅相邻”和“重复覆盖”的区别。
from ipaddress import collapse_addresses, ip_network
def show_case(label: str, values: list[str]) -> None:
# strict=False 只负责把带主机位的输入归一化,不改变合并规则
networks = [ip_network(value, strict=False) for value in values]
result = [str(network) for network in collapse_addresses(networks)]
print(f"{label}: {result}")
show_case("两个对齐的 /25", ["192.0.2.0/25", "192.0.2.128/25"])
show_case("两个未形成更大块的 /26", ["192.0.2.0/26", "192.0.2.128/26"])
show_case("大网覆盖小网", ["198.51.100.0/24", "198.51.100.0/25"])
两个对齐的 /25: ['192.0.2.0/24'] 两个未形成更大块的 /26: ['192.0.2.0/26', '192.0.2.128/26'] 大网覆盖小网: ['198.51.100.0/24']
第二组虽然都在同一个 /24 范围内,但中间还有一段没有覆盖,不能直接变成更大的连续块。判断时不要只比较一个网段的结束地址和另一个网段的起始地址,还要检查它们是否能按同一层级的 CIDR 边界配对。
IPv4 和 IPv6 要分开折叠
官方接口明确要求输入中的网络对象不能混合 IP 版本。实际项目常把两种地址都读进同一个列表,因此最好在调用前按 version 分组,或者在发现混合输入时给出业务层错误。
from collections.abc import Iterable
from ipaddress import IPv4Network, IPv6Network, collapse_addresses, ip_network
def collapse_by_version(values: Iterable[str]) -> dict[int, list[str]]:
# 解析阶段统一得到网络对象,同时保留 Python 的版本属性
networks = [ip_network(value, strict=False) for value in values]
groups: dict[int, list[IPv4Network | IPv6Network]] = {4: [], 6: []}
for network in networks:
# 用 version 分组,避免把 IPv4 和 IPv6 一起传给 collapse_addresses
groups[network.version].append(network)
result: dict[int, list[str]] = {}
for version, group in groups.items():
if group:
# 每组单独折叠,并把迭代器转换成稳定的文本结果
result[version] = [str(network) for network in collapse_addresses(group)]
return result
print(collapse_by_version([
"192.0.2.0/25",
"192.0.2.128/25",
"2001:db8::/65",
"2001:db8:0:0:8000::/65",
]))
{4: ['192.0.2.0/24'], 6: ['2001:db8::/64']}
如果不想分组,也可以在进入函数前直接拒绝同时出现版本 4 和版本 6 的输入。这样错误会更早、更容易定位,避免网络策略层悄悄丢掉其中一类地址。
输出接入配置前要固定边界
collapse_addresses() 返回的是迭代器,所以调用一次后就会被消费。要把结果交给多个下游步骤,先转换成列表;要对文本排序,也应排序网络对象或统一的字符串结果,而不是在多个地方重复遍历原迭代器。
from ipaddress import collapse_addresses, ip_network
def compact_for_config(values: list[str]) -> list[str]:
# 解析并折叠后立刻物化,后续渲染、统计都复用同一份结果
collapsed = list(collapse_addresses(ip_network(value, strict=False) for value in values))
# 网络对象支持按网络地址和前缀进行排序,输出顺序更适合配置 diff
collapsed.sort()
return [network.with_prefixlen for network in collapsed]
print(compact_for_config([
"203.0.113.128/25",
"203.0.113.0/25",
"198.51.100.0/24",
]))
['198.51.100.0/24', '203.0.113.0/24']
排序只是输出层的确定性处理,不会改变折叠规则。若下游把网段作为集合比较,建议把 with_prefixlen 结果作为规范化字符串保存,便于日志、缓存键或配置差异对比。

值班排查时可以按这张清单处理
- 先确认列表元素是网络对象,而不是未解析的字符串或接口对象。
- 读取
network.version,混合 IPv4/IPv6 时按版本分组或提前拒绝。 - 对带主机位的输入决定是否使用
strict=False;这一步属于输入归一化,不是合并本身。 - 把返回迭代器物化一次,记录折叠前后的数量和最终 CIDR 文本。
- 用一对明确对齐的网络做回归样例,再用一个未对齐的相邻样例确认没有过度扩大范围。
常见问题
collapse_addresses() 能直接接受字符串吗?
不要这样用。先通过 ip_network() 把字符串解析为网络对象,再传入折叠函数。
IPv4 和 IPv6 可以放在同一个列表里吗?
不可以混合调用。先按 version 分组,分别折叠,或者在业务层拒绝混合输入。
两个网段地址连续就一定会合并吗?
不一定。它们还必须能组成符合边界对齐要求的更大 CIDR 网络;否则结果会保留多个网段。
为什么我第二次遍历结果为空?
返回值是迭代器,第一次遍历后已经消费。把它转成列表后再交给多个处理步骤。
归纳起来,collapse_addresses() 最适合做“同版本网络对象的 CIDR 归并”:先规范化输入,再分版本折叠,最后物化并固定输出。它负责地址范围的紧凑表达,不负责替你决定路由策略、访问权限或设备配置。
-
434 收藏
-
264 收藏
-
383 收藏
-
141 收藏
-
文章 · python教程 | 1小时前 | 标准库 · 调试 · python · Python 装饰器 functools.wraps functools.update_wrapper __wrapped__ inspect.unwrap174 收藏
-
282 收藏
-
118 收藏
-
148 收藏
-
203 收藏
-
218 收藏
-
文章 · python教程 | 4小时前 | 标准库 · python · ip地址 · Python IPv6 ipv4 CIDR ipaddress summarize_address_range345 收藏
-
153 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习