概述
本指南介绍 Python 标准库的 ipaddress 模块,面向不熟悉 IP 网络术语的用户,也适合希望快速了解其对象模型的网络工程师。
创建 Address、Network 和 Interface 对象
ipaddress 用于检查和操作 IP 地址。开始之前,需要从字符串或整数创建相应对象。以下交互式示例使用 ipaddress 名称,执行前须先 import ipaddress。
关于 IP 版本
互联网协议存在 IPv4 与 IPv6 两个版本。IPv4 地址空间不足,是向 IPv6 迁移的重要原因。详细差异超出本指南范围,但需要知道两个版本同时存在,有时还需要明确选择其中一个。
IP 主机地址
主机地址是 IP 寻址的基本单元。最简单的创建方式是调用 ipaddress.ip_address() 工厂函数,让它根据输入自动选择 IPv4 或 IPv6:
>>> ipaddress.ip_address('192.0.2.1')
IPv4Address('192.0.2.1')
>>> ipaddress.ip_address('2001:DB8::1')
IPv6Address('2001:db8::1')
也可以从整数创建地址;能用32位表示的值默认解释为 IPv4:
>>> ipaddress.ip_address(3221225985)
IPv4Address('192.0.2.1')
>>> ipaddress.ip_address(42540766411282592856903984951653826561)
IPv6Address('2001:db8::1')
直接调用相应的类,可以强制选择 IP 版本。例如,使用较小整数创建 IPv6 地址:
>>> ipaddress.ip_address(1)
IPv4Address('0.0.0.1')
>>> ipaddress.IPv4Address(1)
IPv4Address('0.0.0.1')
>>> ipaddress.IPv6Address(1)
IPv6Address('::1')
定义网络
主机地址通常组成 IP 网络。网络对象可从描述主机地址范围的字符串构造。最简单的形式是“网络地址/网络前缀”:前缀指定需要比较的前导比特数量,网络地址指定这些比特应具有的值。
网络同样提供自动判断版本的工厂函数:
>>> ipaddress.ip_network('192.0.2.0/24')
IPv4Network('192.0.2.0/24')
>>> ipaddress.ip_network('2001:db8::0/96')
IPv6Network('2001:db8::/96')
网络地址的主机位必须为零。因此,192.0.2.1/24 严格来说描述的是网络接口,而非合法的网络地址。默认情况下,设置了主机位会触发 ValueError;传入 strict=False 可以将这些位归零:
>>> ipaddress.ip_network('192.0.2.1/24')
Traceback (most recent call last):
...
ValueError: 192.0.2.1/24 has host bits set
>>> ipaddress.ip_network('192.0.2.1/24', strict=False)
IPv4Network('192.0.2.0/24')
网络也可由整数定义。此时网络只包含该整数表示的一个地址,前缀覆盖地址的全部比特:
>>> ipaddress.ip_network(3221225984)
IPv4Network('192.0.2.0/32')
>>> ipaddress.ip_network(42540766411282592856903984951653826560)
IPv6Network('2001:db8::/128')
也可以直接使用 IPv4Network 或 IPv6Network 构造函数,强制指定版本。
主机接口
如果需要描述某个网络中的具体主机,只使用地址类或网络类还不够。网络工程师常用 192.0.2.1/24 表示“192.0.2.0/24 网络上的主机 192.0.2.1”。接口类将地址与网络关联起来,创建方式与网络对象相同,但地址部分不必是网络地址:
>>> ipaddress.ip_interface('192.0.2.1/24')
IPv4Interface('192.0.2.1/24')
>>> ipaddress.ip_interface('2001:db8::1/96')
IPv6Interface('2001:db8::1/96')
接口类同样接受整数,也支持直接调用对应构造函数以强制指定 IP 版本。
查看对象信息
创建 IPv4 或 IPv6 的 Address、Network、Interface 对象后,可以通过属性获取信息。
获取版本:
>>> addr4 = ipaddress.ip_address('192.0.2.1')
>>> addr6 = ipaddress.ip_address('2001:db8::1')
>>> addr6.version
6
>>> addr4.version
4
从接口获取网络:
>>> host4 = ipaddress.ip_interface('192.0.2.1/24')
>>> host4.network
IPv4Network('192.0.2.0/24')
>>> host6 = ipaddress.ip_interface('2001:db8::1/96')
>>> host6.network
IPv6Network('2001:db8::/96')
查看网络包含的地址总数:
>>> net4 = ipaddress.ip_network('192.0.2.0/24')
>>> net4.num_addresses
256
>>> net6 = ipaddress.ip_network('2001:db8::0/96')
>>> net6.num_addresses
4294967296
遍历可用的主机地址:
>>> net4 = ipaddress.ip_network('192.0.2.0/24')
>>> for x in net4.hosts():
... print(x)
192.0.2.1
192.0.2.2
192.0.2.3
192.0.2.4
...
192.0.2.252
192.0.2.253
192.0.2.254
获取网络掩码和主机掩码。网络掩码对应前缀中的有效位,主机掩码则对应其余位:
>>> net4 = ipaddress.ip_network('192.0.2.0/24')
>>> net4.netmask
IPv4Address('255.255.255.0')
>>> net4.hostmask
IPv4Address('0.0.0.255')
>>> net6 = ipaddress.ip_network('2001:db8::0/96')
>>> net6.netmask
IPv6Address('ffff:ffff:ffff:ffff:ffff:ffff::')
>>> net6.hostmask
IPv6Address('::ffff:ffff')
展开或压缩 IPv6 地址:
>>> addr6.exploded
'2001:0db8:0000:0000:0000:0000:0000:0001'
>>> addr6.compressed
'2001:db8::1'
>>> net6.exploded
'2001:0db8:0000:0000:0000:0000:0000:0000/96'
>>> net6.compressed
'2001:db8::/96'
IPv4 没有对应的展开与压缩形式,但仍提供这两个属性。因此,版本无关的代码可以统一获取简洁或详细表示,同时正确处理两个版本。
将网络视为地址列表
可以通过索引获取网络中的地址:
>>> net4[1]
IPv4Address('192.0.2.1')
>>> net4[-1]
IPv4Address('192.0.2.255')
>>> net6[1]
IPv6Address('2001:db8::1')
>>> net6[-1]
IPv6Address('2001:db8::ffff:ffff')
也可以使用成员测试语法:
if address in network:
# 执行某种操作
包含关系根据网络前缀判断:
>>> addr4 = ipaddress.ip_address('192.0.2.1')
>>> addr4 in ipaddress.ip_network('192.0.2.0/24')
True
>>> addr4 in ipaddress.ip_network('192.0.3.0/24')
False
比较运算
对于有意义的比较,模块提供直观的运算方式:
>>> ipaddress.ip_address('192.0.2.1') < ipaddress.ip_address('192.0.2.2')
True
比较不同 IP 版本或不兼容类型的对象会引发 TypeError。
与其他模块协作
socket 等使用 IP 地址的模块通常不直接接受这些对象,需要转换为字符串或整数:
>>> addr4 = ipaddress.ip_address('192.0.2.1')
>>> str(addr4)
'192.0.2.1'
>>> int(addr4)
3221225985
获取实例创建失败的详细原因
版本无关的工厂函数通常以 ValueError 报告输入无法识别。因为不知道输入本来应当属于 IPv4 还是 IPv6,错误信息较为笼统。
直接调用类构造函数,可以得到 ValueError 的子类 AddressValueError 或 NetmaskValueError,从而明确地址还是掩码解析失败:
>>> ipaddress.ip_address("192.168.0.256")
Traceback (most recent call last):
...
ValueError: '192.168.0.256' does not appear to be an IPv4 or IPv6 address
>>> ipaddress.IPv4Address("192.168.0.256")
Traceback (most recent call last):
...
ipaddress.AddressValueError: Octet 256 (> 255) not permitted in '192.168.0.256'
>>> ipaddress.ip_network("192.168.0.1/64")
Traceback (most recent call last):
...
ValueError: '192.168.0.1/64' does not appear to be an IPv4 or IPv6 network
>>> ipaddress.IPv4Network("192.168.0.1/64")
Traceback (most recent call last):
...
ipaddress.NetmaskValueError: '64' is not a valid netmask
如果不需要区分具体错误,两者都可通过共同父类 ValueError 捕获:
try:
network = ipaddress.IPv4Network(address)
except ValueError:
print('address/netmask is invalid for IPv4:', address)
来源:ipaddress 模块介绍,规范原文链接为 Python HOWTO。作者:Peter Moody、Nick Coghlan。本稿依据官方中文版本完整整理,正文采用 Python 软件基金会许可证第2版;示例、代码片段及其他代码另采用零条款 BSD 许可证。© 2001 Python Software Foundation。详见历史与许可。本次网页读取显示 Python 3.14.7,未将候选库写的3.14.8冒充为本次观测;示例未在本环境运行。











暂无评论内容