news 2026/9/8 20:39:11

Python ipaddress 模块详解:IPv4/IPv6 地址、网络与接口的创建、检查与运算

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python ipaddress 模块详解:IPv4/IPv6 地址、网络与接口的创建、检查与运算

Python ipaddress 模块详解:IPv4/IPv6 地址、网络与接口的创建、检查与运算

【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython

本篇技术指南基于 CPython 标准库 Lib/ipaddress.py 及其官方 API 参考 Doc/library/ipaddress.rst,系统讲解ipaddress模块的全部能力:用工厂函数便捷创建对象、用 IPv4/IPv6 地址/网络/接口对象做子网归属判定、主机遍历、掩码换算、子网切分与聚合,以及地址文本校验与反向 DNS 生成等。读完本文,你将掌握一套既兼容 IPv4 又兼容 IPv6 的纯 Python 地址编程范式,可直接用于防火墙规则判定、配置校验、DHCP 地址池计算、安全扫描与日志归并等真实场景。配套的入门导读可参考 Doc/howto/ipaddress.rst(面向不熟悉 IP 网络术语的读者),本文则为完整 API 参考。

模块定位:创建、操作与检查的三大对象模型

ipaddress自 Python 3.3 起加入标准库(见 Doc/library/ipaddress.rst),提供对 IPv4 与 IPv6 地址、网络的**创建(create)、操作(manipulate)与运算(operate)**能力。常见的典型任务包括:检查两台主机是否位于同一子网、遍历某个子网内的全部主机、判断一段字符串是否为合法的 IP 地址或网络定义等。

模块源码是一个纯 Python 实现、零外部依赖的单文件(Lib/ipaddress.py,约 2400 行),其开头声明为 "A fast, lightweight IPv4/IPv6 manipulation library in Python"。从源码结构(Lib/ipaddress.py)可以梳理出清晰的继承层级:

  • 异常层:AddressValueError(ValueError)NetmaskValueError(ValueError)(第 17、21 行),用于把“地址格式错误”与“掩码格式错误”区分开。
  • 地址层:_BaseAddressIPv4Address(Lib/ipaddress.py)与IPv6Address(Lib/ipaddress.py)。
  • 网络层:_BaseNetworkIPv4Network(Lib/ipaddress.py)与IPv6Network(Lib/ipaddress.py)。
  • 接口层:IPv4Interface(继承IPv4Address,Lib/ipaddress.py)与IPv6Interface(继承IPv6Address,Lib/ipaddress.py),相当于“带网络信息的地址”。

一个值得注意的设计意图是:IPv4AddressIPv6Address共享大量公共属性,IPv6 特有语义的属性在 IPv4 对象上也会以合理方式实现,从而让“一段代码同时正确处理两个 IP 版本”成为可能。所有地址、网络、接口对象都是**可哈希(hashable)**的,可直接用作字典键或放入集合。

便捷工厂函数

模块提供三个工厂函数,自动依据输入判定应返回 IPv4 还是 IPv6 对象。从源码看(Lib/ipaddress.py),它们的实现模式一致:先尝试用对应的 IPv4 构造器,捕获AddressValueError/NetmaskValueError后换 IPv6 构造器再试,两者都失败则抛出合并后的ValueError

>>> import ipaddress >>> ipaddress.ip_address('192.168.0.1') IPv4Address('192.168.0.1') >>> ipaddress.ip_address('2001:db8::') IPv6Address('2001:db8::')
  • ip_address(address):返回IPv4AddressIPv6Address。地址可以是字符串或整数,小于2**32的整数默认按 IPv4 处理;非法输入抛ValueError
  • ip_network(address, strict=True):返回IPv4NetworkIPv6Networkstrict参数原样透传给对应网络构造器;若输入不合法,或网络带主机位且strict=True,抛ValueError
    >>> ipaddress.ip_network('192.168.0.0/28') IPv4Network('192.168.0.0/28')
  • ip_interface(address):返回IPv4InterfaceIPv6Interface,用于描述“位于某个网络上的一个接口地址”。

这些便利函数的共同代价是:因为函数不知道调用者本意是 IPv4 还是 IPv6 格式,失败时的报错信息只能给出笼统描述。需要更精确的错误信息时,应当直接调用对应版本的具体类构造器(详见下文“自定义异常”一节),这正是AddressValueErrorNetmaskValueError存在的意义。

IP 地址对象

合法输入形式

IPv4Address(address)(见 Lib/ipaddress.py)接受三种形式的输入,非法输入抛AddressValueError

  1. 十进制点分字符串:四个取值 0–255 的十进制整数用点分隔,如192.168.0.1,每个整数代表地址中的一个字节(octet)。不允许前导零,以免与八进制记法混淆(详见下方版本说明);
  2. 可放入 32 位的整数
  3. 长度 4 的bytes打包对象(最高有效字节在前)。
>>> ipaddress.IPv4Address('192.168.0.1') IPv4Address('192.168.0.1') >>> ipaddress.IPv4Address(3232235521) IPv4Address('192.168.0.1') >>> ipaddress.IPv4Address(b'\xC0\xA8\x00\x01') IPv4Address('192.168.0.1')

关于前导零的演进史值得特别注意:3.8 之前曾容忍前导零(包括形似八进制的歧义写法),3.9.5 起前导零一律视为错误,IPv4 字符串的解析严格程度与 glibc 的socket.inet_pton保持一致。

IPv6Address(address)(见 Lib/ipaddress.py)接受三种形式,非法输入抛AddressValueError

  1. 字符串:八组各 16 bit 的十六进制组,以冒号分隔(exploded全展开记法),或采用 RFC 4291 定义的多种压缩(compressed简写)规则,如"0000:0000:0000:0000:0000:0abc:0007:0def"可压缩为"::abc:7:def"。字符串可选携带作用域区 ID 后缀%scope_id(见 RFC 4007):区 ID 必须非空且不能含%,例如fe80::1234%1表示本节点第一条链路上的fe80::1234
  2. 可放入 128 位的整数
  3. 长度 16 的bytes打包对象,大端序。
>>> ipaddress.IPv6Address('2001:db8::1000') IPv6Address('2001:db8::1000') >>> ipaddress.IPv6Address('ff02::5678%1') IPv6Address('ff02::5678%1')

通用属性(IPv4/IPv6 共用)

属性说明
version版本号:IPv4 为4,IPv6 为6(自 3.14 起同时以类属性形式暴露)
max_prefixlen地址总位数:IPv4 为32,IPv6 为128(自 3.14 起同时以类属性形式暴露);前缀即决定地址是否属于某网络时被比较的“高位位数”
compressed/exploded地址的字符串表示。IPv4 没有针对零字节的简写记法,故两属性恒等于str(addr);同时暴露二者是为了让显示代码可统一处理两种版本
packed二进制表示:IPv4 为 4 字节、IPv6 为 16 字节的bytes(最高有效字节在前)
reverse_pointer反向 DNS PTR 记录名(仅是可用于 PTR 查询的名字,不是解析出的主机名),自 3.5 起提供
is_multicast是否多播保留地址(IPv4 见 RFC 3171,IPv6 见 RFC 2373)
is_private按 IANA 特殊地址注册表判定是否“全局不可达”
is_globalis_private相反
is_unspecified是否未指定地址(IPv4 见 RFC 5735,IPv6 见 RFC 2373)
is_reserved是否 IETF 保留地址。IPv4 仅指240.0.0.0/4保留块;IPv6 指地址空间中Reserved by IETF的全部地址。注意 IPv6 的fec0::/10(旧 Site-Local)不在其中(参见is_site_local与 RFC 3879)
is_loopback是否回环地址(IPv4 见 RFC 3330,IPv6 见 RFC 2373)
is_link_local是否链路本地保留地址(RFC 3927)
ipv6_mapped返回表示“IPv4 映射的 IPv6 地址”的IPv4Address对象(RFC 4291),自 3.13 起提供

关于is_private/is_global的语义,有两点官方明示的例外:

  • 共享地址空间100.64.0.0/10上两者都为False(其余地址上两者恰好相反);
  • 对 IPv4-mapped IPv6 地址,其判定由底层 IPv4 语义决定,恒满足address.is_private == address.ipv4_mapped.is_privateis_global同理)。

3.13 版本对is_private做过一次修正(同时影响is_global),修正了一批误判:

  • IPv4:192.0.0.0/24视为私有时段从原来的仅192.0.0.0/29扩展为整段,但192.0.0.9/32192.0.0.10/32例外(192.0.0.9即任播地址);
  • IPv6:64:ff9b:1::/482002::/16视为私有;2001::/23整体视为私有,但其中2001:1::1/1282001:1::2/1282001:3::/322001:4:112::/482001:20::/282001:30::/28例外,不视为私有。

从实现上可以印证:上述常量直接编码在源码的_IPv6Constants._private_networks_private_networks_exceptions两个列表中(Lib/ipaddress.py),网络对象的is_private则要求网络地址与广播地址同时命中私有区间、且同时不属于例外区间(Lib/ipaddress.py)。其反向指针、loopback、multicast 等输出示例:

>>> ipaddress.ip_address("127.0.0.1").reverse_pointer '1.0.0.127.in-addr.arpa' >>> ipaddress.ip_address("2001:db8::1").reverse_pointer '1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa'

IPv6 特有属性

  • is_site_local:是否站点本地保留地址。站点本地地址空间已被 RFC 3879 废弃,判断是否属于 RFC 4193 的唯一本地地址应改用is_private
  • ipv4_mapped:对位于::FFFF:0:0/96(RFC 4291)的 IPv4-mapped 地址,返回内嵌的 IPv4 地址;其余地址返回None
  • scope_id:RFC 4007 作用域地址的区 ID(字符串形式);未指定时返回Nonetest_ipaddress.py中有专门针对 IPv6 scoped 地址比较与字符串化的测试(Lib/test/test_ipaddress.py)。
  • sixtofour:对2002::/16内的 6to4 地址(RFC 3056),返回内嵌 IPv4 地址;否则None
  • teredo:对2001::/32内的 Teredo 地址(RFC 4380),返回内嵌的(server, client)地址对;否则None

字符串与整数互转

socket等网络接口互操作时,需用内建str()/int()转换:

>>> str(ipaddress.IPv4Address('192.168.0.1')) '192.168.0.1' >>> int(ipaddress.IPv4Address('192.168.0.1')) 3232235521 >>> str(ipaddress.IPv6Address('::1')) '::1' >>> int(ipaddress.IPv6Address('::1')) 1

注意:IPv6 作用域地址转整数时会丢弃%scope_id。反向包装可由v4_int_to_packed/v6_int_to_packed完成(见模块级函数一节)。

地址运算符

比较运算符只能在同版本对象间使用(IPv4 对 IPv4,IPv6 对 IPv6)。带不同 scope zone ID 的相同 IPv6 地址不相等

>>> IPv4Address('127.0.0.2') > IPv4Address('127.0.0.1') True >>> IPv4Address('127.0.0.2') == IPv4Address('127.0.0.1') False >>> IPv6Address('fe80::1234') == IPv6Address('fe80::1234%1') False >>> IPv6Address('fe80::1234%1') != IPv6Address('fe80::1234%2') True

算术运算符:地址支持整数加减,越界时抛AddressValueError

>>> IPv4Address('127.0.0.2') + 3 IPv4Address('127.0.0.5') >>> IPv4Address('127.0.0.2') - 3 IPv4Address('126.255.255.255') >>> IPv4Address('255.255.255.255') + 1 Traceback (most recent call last): ... ipaddress.AddressValueError: 4294967296 (>= 2**32) is not permitted as an IPv4 address

自定义格式化:__format__

自 3.9 起,地址对象支持format()str.format()与 f-string 的格式化协议。格式符fmt取值如下:'s'(默认,等价于str())、'b'(零填充二进制串)、'X'/'x'(大小写十六进制)、'n'(IPv4 等价于'b',IPv6 等价于'x')。二进制与十六进制形式还支持'#'前缀符与'_'千分位分组符。

>>> format(ipaddress.IPv4Address('192.168.0.1')) '192.168.0.1' >>> '{:#b}'.format(ipaddress.IPv4Address('192.168.0.1')) '0b11000000101010000000000000000001' >>> f'{ipaddress.IPv6Address("2001:db8::1000"):s}' '2001:db8::1000' >>> format(ipaddress.IPv6Address('2001:db8::1000'), '_X') '2001_0DB8_0000_0000_0000_0000_0000_1000' >>> '{:#_n}'.format(ipaddress.IPv6Address('2001:db8::1000')) '0x2001_0db8_0000_0000_0000_0000_0000_1000'

test_format在两个版本的地址测试类中均有对应覆盖(Lib/test/test_ipaddress.py、Lib/test/test_ipaddress.py)。

IP 网络对象

IPv4Network/IPv6Network用于定义并检查 IP 网段。一个网络定义由**掩码(mask)网络地址(network address)**组成,二者共同划定一段区间:区间内任一地址与掩码做“按位与(binary AND)”后都等于网络地址。例如掩码255.255.255.0、网络地址192.168.1.0的网络,包含192.168.1.0192.168.1.255闭区间内全部地址。

网络对象继承地址对象的全部属性(versionis_*系列等),且这些布尔属性“对整个网络成立”当且仅当网络地址与广播地址同时成立。地址对象与网络对象均为可哈希对象。

前缀、网络掩码与主机掩码的换算

三种等价的掩码记法贯穿本模块全部 API:

  • 前缀(prefix)/nbits:表示掩码高位置 1 的位数。IPv4 的/24等价于网络掩码255.255.255.0,IPv6 等价于ffff:ff00::
  • 网络掩码(net mask):高位连续置 1 的地址形式;
  • 主机掩码(host mask):网络掩码的逻辑反,常用于思科访问控制列表,IPv4 中/24的主机掩码为0.0.0.255

构造形式与strict语义

IPv4Network(address, strict=True)address可以是以下四种:

  1. “IP 地址 + 可选掩码”的字符串(以/分隔):掩码要么是单个数字(即前缀),要么是点分 IPv4 字符串——后者若首字段非零网络掩码解释,若首字段为零则按主机掩码解释(唯一例外是全零掩码,按网络掩码处理);不带掩码时默认/32。因此下面三种写法等价:
    192.168.1.0/24 == 192.168.1.0/255.255.255.0 == 192.168.1.0/0.0.0.255
  2. 可放入 32 位的整数:等价于网络地址为该整数、掩码/32的单地址网络;
  3. 长度 4 的大端bytes:解释同整数形式;
  4. 二元组(地址描述, 掩码)(3.5 起支持):地址描述可以是字符串、32 位整数、4 字节打包整数或现成的IPv4Address对象;掩码是前缀长度整数(如24)或前缀掩码字符串(如255.255.255.0)。

地址非法抛AddressValueError,掩码非法抛NetmaskValueErrorstrict=True时若所给地址带主机位则抛ValueError;否则主机位会被掩掉以得到规范网络地址。例如ip_network('192.168.1.1/24')在默认strict=True下会报错,需显式strict=False或改用接口对象。

未另行说明时,接受其它网络/地址对象的网络方法,在参数 IP 版本不兼容时会抛TypeError

IPv6Network(address, strict=True)的输入形式与之基本平行,但有两点差异:

  1. 字符串形式只接受前缀长度数字,当前不支持展开的网络掩码2001:db00::0/24合法,而2001:db00::0/ffff:ff00::不合法;缺省前缀视为/128
  2. 二元组中的掩码仅支持整数前缀长度。

网络核心属性

属性说明
network_address网络地址,与前缀长度共同唯一确定一个网络
broadcast_address广播地址,发往该地址的数据包应被网络上每个主机接收
netmask/hostmask网络掩码 / 主机掩码,均为地址对象
prefixlen前缀长度(比特数)
num_addresses网络内地址总数
with_prefixlen/compressed/exploded掩码用前缀记法的字符串表示;with_prefixlencompressed恒等于str(network)exploded用全展开形式
with_netmask网络掩码记法的字符串表示
with_hostmask主机掩码记法的字符串表示

底层实现值得留意:broadcast_addresshostmask都用functools.cached_property惰性计算并缓存(Lib/ipaddress.py),前者是network_address | hostmask,后者是netmask ^ 全1num_addresses则是广播地址与网络地址之差加一(Lib/ipaddress.py)。由于网络较大时(如 IPv6/64)遍历会不可行,这些属性是 O(1) 计算,绝不遍历。

网络方法

hosts():返回“可用主机”迭代器——IPv4 下排除网络地址与广播地址本身;/31网络把网络地址与广播地址也纳入可用;/32只返回该单主机(Lib/ipaddress.py)。

>>> list(ip_network('192.0.2.0/29').hosts()) [IPv4Address('192.0.2.1'), ..., IPv4Address('192.0.2.6')] >>> list(ip_network('192.0.2.0/31').hosts()) [IPv4Address('192.0.2.0'), IPv4Address('192.0.2.1')] >>> list(ip_network('192.0.2.1/32').hosts()) [IPv4Address('192.0.2.1')]

IPv6 的hosts()语义略有不同(Lib/ipaddress.py):排除的是Subnet-Router anycast 地址(网络地址本身);/127网络把该 anycast 地址也纳入;/128返回单个主机。

overlaps(other):两者任一全部/部分包含对方即返回True。其实现是把“对方网络地址或广播地址落在己方”与“己方地址落在对方”四种情形各判一次(Lib/ipaddress.py)。

address_exclude(network):从本网络中剔除给定子网,返回剩余部分的网络对象迭代器;若给定网络未完全包含在本网络内则抛ValueError。其实现基于二分递归,用能覆盖差集的最小网段表示“减法”结果:

>>> n1 = ip_network('192.0.2.0/28') >>> n2 = ip_network('192.0.2.1/32') >>> list(n1.address_exclude(n2)) [IPv4Network('192.0.2.8/29'), IPv4Network('192.0.2.4/30'), IPv4Network('192.0.2.2/31'), IPv4Network('192.0.2.0/32')]

subnets(prefixlen_diff=1, new_prefix=None):把当前网络切分为若干子网。prefixlen_diff表示前缀长度增加的位数;new_prefix直接指定子网期望前缀(必须比当前前缀长)。二者必须且只能设置其一。返回迭代器:

>>> list(ip_network('192.0.2.0/24').subnets()) [IPv4Network('192.0.2.0/25'), IPv4Network('192.0.2.128/25')] >>> list(ip_network('192.0.2.0/24').subnets(prefixlen_diff=2)) [IPv4Network('192.0.2.0/26'), IPv4Network('192.0.2.64/26'), IPv4Network('192.0.2.128/26'), IPv4Network('192.0.2.192/26')] >>> list(ip_network('192.0.2.0/24').subnets(new_prefix=26)) [IPv4Network('192.0.2.0/26'), IPv4Network('192.0.2.64/26'), IPv4Network('192.0.2.128/26'), IPv4Network('192.0.2.192/26')] >>> ip_network('192.0.2.0/24').subnets(new_prefix=23) Traceback (most recent call last): ... ValueError: new prefix must be longer

supernet(prefixlen_diff=1, new_prefix=None):求包含当前网络的最小上层网络。prefixlen_diff为前缀减少位数,new_prefix直接指定期望前缀(必须比当前前缀短)。同样二选一。返回单个网络对象:

>>> ip_network('192.0.2.0/24').supernet() IPv4Network('192.0.2.0/23') >>> ip_network('192.0.2.0/24').supernet(prefixlen_diff=2) IPv4Network('192.0.0.0/22') >>> ip_network('192.0.2.0/24').supernet(new_prefix=20) IPv4Network('192.0.0.0/20')

subnet_of(other)/supernet_of(other)(均自 3.7 起):判断本网络是否是另一网络的子网 / 超网:

>>> a = ip_network('192.168.1.0/24') >>> b = ip_network('192.168.1.128/30') >>> b.subnet_of(a) True >>> a.supernet_of(b) True

compare_networks(other):仅比较网络地址(不看主机位),返回-1/0/1。3.7 起标记为 deprecated——它使用与<==>完全一致的排序与比较算法,直接用这些运算符即可:

>>> ip_network('192.0.2.1/32').compare_networks(ip_network('192.0.2.2/32')) -1 >>> ip_network('192.0.2.1/32').compare_networks(ip_network('192.0.2.0/32')) 1 >>> ip_network('192.0.2.1/32').compare_networks(ip_network('192.0.2.1/32')) 0

next_network(next_prefix=None)(自 3.16 起新增,见 Lib/ipaddress.py):求与当前网络“紧邻的下一个”指定前缀大小网络。next_prefix缺省时沿用当前前缀;它必须在1max_prefixlen之间,否则抛ValueError;若地址空间耗尽则同样抛ValueError。这非常适合做地址池的顺序分配:

>>> IPv4Network('192.0.2.0/24').next_network() IPv4Network('192.0.3.0/24') >>> IPv4Network('192.0.2.0/24').next_network(next_prefix=25) IPv4Network('192.0.3.0/25') >>> IPv4Network('192.0.2.0/24').next_network(next_prefix=23) IPv4Network('192.0.4.0/23') >>> IPv4Network('192.0.80.0/22').next_network(next_prefix=18) IPv4Network('192.0.128.0/18') >>> IPv4Network('255.255.255.0/24').next_network() Traceback (most recent call last): ... ValueError: out of address space, cannot make another /24 network

subnet_of/supernet_of等在 Lib/test/test_ipaddress.py 等测试方法中有系统覆盖;测试还专门验证了跨版本混用(v4 与 v6)时subnet_of等应安全返回False或抛TypeError(见 Lib/test/test_ipaddress.py)。

网络运算符、迭代与容器行为

网络对象支持逻辑比较运算符,排序规则是先按网络地址、再按网络掩码==的实现要求版本、网络地址与掩码三者都一致(Lib/ipaddress.py)。

迭代网络对象会得到网络内全部地址(含网络地址与广播地址这些不可用地址;只想要可用主机请用hosts())。其实现是range(network, broadcast+1)的整数步进生成(Lib/ipaddress.py):

>>> for addr in IPv4Network('192.0.2.0/28'): ... addr ... IPv4Address('192.0.2.0') ... IPv4Address('192.0.2.15')

网络还是地址的容器:支持下标索引与in成员测试(__getitem____contains__实现见 Lib/ipaddress.py)。成员测试的判定规则是:目标地址与网络掩码按位与的结果等于网络地址;版本不同时永远为False

>>> IPv4Network('192.0.2.0/28')[0] IPv4Address('192.0.2.0') >>> IPv4Network('192.0.2.0/28')[15] IPv4Address('192.0.2.15') >>> IPv4Address('192.0.2.6') in IPv4Network('192.0.2.0/28') True >>> IPv4Address('192.0.3.6') in IPv4Network('192.0.2.0/28') False

接口对象:地址 + 网络信息的组合

IPv4Interface(address)IPv6Interface(address)描述“挂在某网络上的一张接口”。其address参数的语义与对应网络构造器相同,但任意主机地址总是被接受(不会因带主机位而报错)。IPv4InterfaceIPv4Address的子类,因此继承地址类全部属性;额外提供如下属性(IPv6 对应类 Lib/ipaddress.py 语义相同):

  • ip:剥离网络信息后的纯地址对象:
    >>> interface = IPv4Interface('192.0.2.5/24') >>> interface.ip IPv4Address('192.0.2.5')
  • network:该接口所属网络:
    >>> interface.network IPv4Network('192.0.2.0/24')
  • with_prefixlen/with_netmask/with_hostmask:三种掩码记法的接口字符串:
    >>> interface.with_prefixlen '192.0.2.5/24' >>> interface.with_netmask '192.0.2.5/255.255.255.0' >>> interface.with_hostmask '192.0.2.5/0.0.0.255'

接口的比较语义比地址、网络更精细(详见 Doc/library/ipaddress.rst):

  • 判等(==/!=):IP 地址与网络都必须相同才相等;接口对象与任何地址对象或网络对象都永不相等的
  • 排序(</>等):同版本的接口与地址对象可以比较,地址对象总是排在接口对象之前;两个接口对象先按各自网络比较,网络相同时再按 IP 地址比较。
>>> IPv4Interface('192.0.2.5/24') == IPv4Address('192.0.2.5') False >>> IPv4Interface('192.0.2.5/24') == IPv4Interface('192.0.2.5/24') True

其它模块级函数

函数功能
v4_int_to_packed(address)把 IPv4 整数地址转为 4 字节大端bytes;负数或超 32 位抛ValueError
v6_int_to_packed(address)把 IPv6 整数地址转为 16 字节大端bytes;越界抛ValueError
summarize_address_range(first, last)[first, last]闭区间内的连续地址聚合成尽量少且对齐的网段,返回迭代器
collapse_addresses(addresses)将一批网络对象合并为尽可能少的相邻网段,返回迭代器;输入混入不同版本抛TypeError
get_mixed_type_key(obj)为地址与网络混排的排序提供 key 函数

用法示例:

>>> ipaddress.ip_address(3221225985) IPv4Address('192.0.2.1') >>> ipaddress.v4_int_to_packed(3221225985) b'\xc0\x00\x02\x01' >>> [ipaddr for ipaddr in ipaddress.summarize_address_range( ... ipaddress.IPv4Address('192.0.2.0'), ... ipaddress.IPv4Address('192.0.2.130'))] [IPv4Network('192.0.2.0/25'), IPv4Network('192.0.2.128/31'), IPv4Network('192.0.2.130/32')] >>> [ipaddr for ipaddr in ... ipaddress.collapse_addresses([ipaddress.IPv4Network('192.0.2.0/25'), ... ipaddress.IPv4Network('192.0.2.128/25')])] [IPv4Network('192.0.2.0/24')]

summarize_address_rangefirst/last非地址对象或版本不同时抛TypeErrorlast不大于first或版本号非法时抛ValueError。这类“区间聚合/反聚合”功能在配置防火墙白名单、生成 ACL 汇总条目时非常实用。

get_mixed_type_key的典型用法是配合sorted()。由于地址与网络“本质不同”,IPv4Address('192.0.2.0') <= IPv4Network('192.0.2.0/24')这种比较默认没有意义、会抛异常;但你仍然可以借助该函数强制排序:

>>> sorted([IPv4Network('192.0.2.0/24'), IPv4Address('192.0.2.5')], ... key=ipaddress.get_mixed_type_key) [IPv4Address('192.0.2.5'), IPv4Network('192.0.2.0/24')]

(行为示意:地址按数值排序在前,网络随后。规则细节以官方文档及 doctest 为准。)

自定义异常与更精确的错误定位

模块为支持类构造器更具体的错误报告,定义了继承自ValueError的两个异常:

  • AddressValueError:与地址取值相关的错误;
  • NetmaskValueError:与网络掩码相关的错误。

工厂函数由于需同时尝试 IPv4 与 IPv6,其合并后的ValueError信息往往较为笼统(例如'...' does not appear to be an IPv4 or IPv6 address,见 Lib/ipaddress.py);而直接调用版本化构造器可以精确得到“地址段坏”还是“掩码段坏”。例如IPv4Address('192.168.1.256')AddressValueErrorIPv4Network('192.168.1.0/33')NetmaskValueError。写代码时可据此分别捕获并给出不同的用户提示。二者定义在 Lib/ipaddress.py,构造器地址/掩码错误消息的专测集中在 Lib/test/test_ipaddress.py 的FactoryFunctionErrors及网络测试类中。

版本演进要点速查

把散落在文档中的versionadded/versionchanged汇总,便于升级适配:

  • 3.3:模块加入标准库;ip_address/ip_network/ip_interface工厂函数、地址/网络/接口对象齐备。
  • 3.4is_global属性加入。
  • 3.5reverse_pointer加入;网络构造器新增“二元组 (地址, 掩码)”形式。
  • 3.7subnet_of/supernet_of加入;compare_networks标记为 deprecated。
  • 3.8:容忍 IPv4 字符串前导零(含形似八进制的写法)。
  • 3.9__format__格式化协议加入;v4_int_to_packed等示例行为定型。
  • 3.9.5:IPv4 字符串不再容忍前导零,解析严格程度对齐 glibcinet_pton
  • 3.13:修正is_private/is_global的一批误判(见前文列表);IPv4Address.ipv6_mapped加入。
  • 3.14versionmax_prefixlen同时以类属性形式暴露。
  • 3.16next_network()加入。

测试与进一步研读

仓库为ipaddress准备了约 2900 行的单元测试 Lib/test/test_ipaddress.py。测试按“v4/v6 公共测试混入 + 版本化测试类”组织:CommonTestMixin_v4/CommonTestMixin_v6(Lib/test/test_ipaddress.py)承载两版本共享的断言逻辑,AddressTestCase_v4/v6(L196/L316)、InterfaceTestCase_v4/v6(L669/L832)、NetworkTestCase_v4/v6(L673/L836)分别针对地址/接口/网络三类对象,另有ComparisonTests(L902)与IpaddrUnitTest(L1083)负责跨类比较与历史回归。安装并构建好本仓库的解释器后,可运行:

./python -m test test_ipaddress

若只运行部分用例,可加-m指定测试方法名,例如:

./python -m test test_ipaddress -m test_next_network

阅读源码时建议沿“文档 → 实现 → 测试”的闭环:先在 Doc/library/ipaddress.rst 查阅某 API 的官方语义,再在 Lib/ipaddress.py 中看具体算法(前缀解析_split_optional_netmask_make_netmask、子网切分、summarize_address_range的区间对齐逻辑等),最后到 Lib/test/test_ipaddress.py 找对应断言来确认边界行为。这样既能获得权威定义,又能理解每个方法为何这样实现,从而在防火墙规则校验、网络扫描、DHCP/地址池管理、配置模板校验等业务中可靠地使用这套 API。

【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/8 20:38:25

Windows Terminal 自动补全实战:PSReadLine 与 Clink 组合配置指南

1. 先搞明白&#xff1a;Windows Terminal 的自动补全到底缺什么这些年不管是从 cmd 迁移过来&#xff0c;还是从 macOS 的 iTerm2 转战 Windows&#xff0c;很多人装上 Windows Terminal 的第一反应都是&#xff1a;界面是漂亮了&#xff0c;字体渲染也舒服了&#xff0c;可这…

作者头像 李华
网站建设 2026/9/8 20:37:03

嵌入式音频解码中心SDK解析:标准C实现多路输入路由与缓冲机制

简介&#xff1a;这套C语言编写的声道解码SDK&#xff0c;面向音频设备开发与嵌入式软件工程师&#xff0c;解决HDMI、光纤、同轴、模拟、U盘、TF/SD卡及话筒输入等多类音源信号的统一解码问题。压缩包共46个文件&#xff0c;既包含C源码头文件与静态库&#xff0c;也附带PDF用…

作者头像 李华
网站建设 2026/9/8 20:36:51

ESP-IDF v5.4.1 环境搭建避坑:从零到第一次编译

ESP-IDF v5.4.1 环境搭建避坑&#xff1a;从零到第一次编译 【免费下载链接】esp-idf Espressif IoT Development Framework. Official development framework for Espressif SoCs. 项目地址: https://gitcode.com/GitHub_Trending/es/esp-idf 第一次装 ESP-IDF&#xf…

作者头像 李华