ipaddress模块 — IP地址处理

Python标准库精讲专题 · 网络通信篇 · 掌握IP地址处理

专题:Python标准库精讲系统学习

关键词:Python, 标准库, ipaddress, IP地址, IPv4, IPv6, 子网, CIDR, 网络地址, 地址验证

一、IP地址概述

IP地址是互联网协议的基石,用于标识网络中的每一台设备。Python的 ipaddress 模块提供了一套完整的工具,用于创建、操作和验证IP地址与网络。该模块自Python 3.3起被纳入标准库,极大简化了网络编程中的地址处理工作。

IPv4与IPv6

互联网协议目前存在两个版本:IPv4 和 IPv6。IPv4 地址为32位,通常以点分十进制表示(如 192.168.1.1),提供约43亿个地址,随着互联网规模爆炸式增长,地址资源已近枯竭。IPv6 地址为128位,以冒号分隔的十六进制表示(如 2001:db8::1),提供了几乎无限的地址空间。ipaddress 模块同时对两个版本提供了完整的支持。

CIDR表示法

无类域间路由(Classless Inter-Domain Routing, CIDR)是现代IP地址分配和路由的核心机制。它使用 / 后跟网络前缀长度的表示法,例如 192.168.1.0/24 表示前24位为网络位,后8位为主机位。CIDR 取代了传统的基于类(A/B/C类)的地址划分,提高了地址空间的利用率。在 ipaddress 模块中,CIDR 表示法被广泛用于构造网络对象和进行子网操作。

核心概念速览:IPv4地址32位,约43亿个;IPv6地址128位,几乎无限。CIDR格式如 10.0.0.0/8 表示一个网络,其中 /8 表示网络前缀长度。

二、IPv4Address/IPv6Address — 地址对象

IP地址对象是整个模块的基础。无论是 IPv4 还是 IPv6 地址,都可以通过 ipaddress.ip_address() 工厂函数或对应的具体类来创建。地址对象一经创建即不可变,并提供了丰富的属性用于查询地址的特性。

构造函数

ipaddress.IPv4Address(address)ipaddress.IPv6Address(address) 接受字符串或整数形式的地址。工厂函数 ipaddress.ip_address(address) 会自动判断地址版本并返回对应的对象。

import ipaddress # 字符串构造 addr1 = ipaddress.IPv4Address('192.168.1.1') addr2 = ipaddress.IPv6Address('2001:db8::1') # 整数构造(IPv4的32位整数表示) addr3 = ipaddress.IPv4Address(3232235777) # 3232235777 = 192.168.1.1 # 工厂函数自动识别版本 addr4 = ipaddress.ip_address('10.0.0.1') # => IPv4Address addr5 = ipaddress.ip_address('::1') # => IPv6Address (环回地址) print(addr1) # 192.168.1.1 print(addr3) # 192.168.1.1

核心属性

IP地址对象提供了丰富的只读属性,用于获取地址的版本信息、压缩/展开格式、以及判断地址类型。

属性说明示例结果 (IPv4 192.168.1.1)
versionIP协议版本号4
max_prefixlen该版本的最大前缀长度32
compressed压缩格式字符串192.168.1.1
exploded展开格式字符串192.168.1.1
packed网络字节序的bytes表示b'\xc0\xa8\x01\x01'
is_private是否为私有地址True
is_global是否为全局公网地址False
is_multicast是否为组播地址False
is_loopback是否为环回地址False
is_link_local是否为链路本地地址False
is_unspecified是否为未指定地址 (0.0.0.0)False
is_reserved是否为保留地址False
addr = ipaddress.IPv4Address('127.0.0.1') print(addr.is_loopback) # True print(addr.is_private) # False(127.0.0.1是环回,不是私有) addr2 = ipaddress.IPv4Address('224.0.0.1') print(addr2.is_multicast) # True # IPv6 示例 v6addr = ipaddress.IPv6Address('fe80::1') print(v6addr.is_link_local) # True v6addr2 = ipaddress.IPv6Address('ff02::1') print(v6addr2.is_multicast) # True # IPv6 特有属性 print(v6addr2.ipv4_mapped) # None (非 IPv4 映射地址) print(v6addr2.sixtofour) # None print(v6addr2.teredo) # None

字符串与整数互转

将IP地址转为整数在网络计算中非常有用,例如子网计算、IP地址排序等。 ipaddress 模块通过 int() 内置函数即可将地址对象转为整数,也可以直接比较大小。

# 地址转整数 addr = ipaddress.IPv4Address('192.168.1.1') num = int(addr) print(num) # 3232235777 # 整数转地址 addr2 = ipaddress.IPv4Address(3232235777) print(addr2) # 192.168.1.1 # 比较运算(按整数值比较) print(ipaddress.IPv4Address('192.168.1.10') > ipaddress.IPv4Address('192.168.1.1')) # True # IPv6 同样支持 v6 = ipaddress.IPv6Address('::ffff:192.168.1.1') print(int(v6)) # 281472812449793

提示:IPv6Address 的 exploded 属性会将地址以完整的冒号分隔十六进制格式呈现,包含所有前导零,而 compressed 属性则会省略连续的零段。例如 ::1 的 exploded 格式为 0:0:0:0:0:0:0:1

# IPv6 压缩与展开格式对比 v6 = ipaddress.IPv6Address('2001:db8::1') print(v6.compressed) # 2001:db8::1 print(v6.exploded) # 2001:0db8:0000:0000:0000:0000:0000:0001

三、IPv4Network/IPv6Network — 网络对象

网络对象表示一个IP子网,包含地址范围和子网掩码信息。通过 ipaddress.ip_network(address, strict=True) 工厂函数或对应的具体类创建。与地址对象不同,网络对象构造时默认进行严格检查,确保传入的地址是该网络的网络地址(主机位全零)。

# 创建网络对象(严格模式) net1 = ipaddress.IPv4Network('192.168.1.0/24') print(net1) # 192.168.1.0/24 # 非严格模式:传入非网络地址时自动修正 net2 = ipaddress.IPv4Network('192.168.1.100/24', strict=False) print(net2) # 192.168.1.0/24(自动将主机位清零) # 严格模式下传入非网络地址会抛出异常 # ipaddress.IPv4Network('192.168.1.100/24') # => ValueError: 192.168.1.100/24 has host bits set

网络边界属性

网络对象提供了多个属性来获取网络范围的关键地址:网络地址(network_address)、广播地址(broadcast_address)、子网掩码(netmask)、反掩码(hostmask)以及前缀长度(prefixlen)。

属性说明示例 (192.168.1.0/24)
network_address网络地址(主机位全零)192.168.1.0
broadcast_address广播地址(主机位全一)192.168.1.255
netmask子网掩码255.255.255.0
hostmask反掩码(主机位掩码)0.0.0.255
prefixlen网络前缀长度24
num_addresses网络中的地址总数256
versionIP协议版本4
max_prefixlen该版本最大前缀长度32
with_netmask带子网掩码的字符串表示192.168.1.0/255.255.255.0
with_prefixlen带前缀长度的字符串表示192.168.1.0/24
with_hostmask带反掩码的字符串表示192.168.1.0/0.0.0.255
is_private是否为私有网络True
net = ipaddress.IPv4Network('10.0.0.0/16') print(f"网络地址: {net.network_address}") # 10.0.0.0 print(f"广播地址: {net.broadcast_address}") # 10.0.255.255 print(f"子网掩码: {net.netmask}") # 255.255.0.0 print(f"反掩码: {net.hostmask}") # 0.0.255.255 print(f"前缀长度: {net.prefixlen}") # 16 print(f"地址总数: {net.num_addresses}") # 65536 print(f"是否为私有: {net.is_private}") # True

子网划分与超网合并

subnets(prefixlen_diff=1) 方法将一个网络划分为更小的子网。supernet(prefixlen_diff=1) 方法则向上寻找包含当前网络的超网。这两个方法是网络规划中的核心工具。

net = ipaddress.IPv4Network('192.168.0.0/24') # 划分为2个/25子网 subs = list(net.subnets(prefixlen_diff=1)) for sub in subs: print(sub) # 192.168.0.0/25 # 192.168.0.128/25 # 划分为4个/26子网 subs4 = list(net.subnets(prefixlen_diff=2)) for sub in subs4: print(sub) # 192.168.0.0/26 # 192.168.0.64/26 # 192.168.0.128/26 # 192.168.0.192/26 # subnets() 也可以指定新的前缀长度 subs_new = list(net.subnets(new_prefix=26)) # 同上,4个/26子网 # 超网查询 super_net = net.supernet(prefixlen_diff=1) print(super_net) # 192.168.0.0/23 # 多次超网合并 super_net2 = net.supernet(prefixlen_diff=8) print(super_net2) # 192.168.0.0/16

地址迭代器与子网排除

hosts() 方法返回一个迭代器,遍历该网络中所有可用的主机地址(排除网络地址和广播地址)。address_exclude(network) 方法返回从当前网络中排除指定子网后剩余的地址块,是网络规划中非常实用的功能。

# hosts() 遍历可用主机地址 net = ipaddress.IPv4Network('192.168.1.0/28') # 16个地址,14个可用主机 hosts = list(net.hosts()) print(len(hosts)) # 14(排除.0网络地址和.15广播地址) print(hosts[0]) # 192.168.1.1 print(hosts[-1]) # 192.168.1.14 # address_exclude() 排除子网 net = ipaddress.IPv4Network('10.0.0.0/24') exclude_net = ipaddress.IPv4Network('10.0.0.0/25') remaining = list(net.address_exclude(exclude_net)) for r in remaining: print(r) # 10.0.0.128/25(排除前一半后剩下的地址块) # 排除中间子网 net = ipaddress.IPv4Network('10.0.0.0/24') exclude_net = ipaddress.IPv4Network('10.0.0.64/26') remaining = list(net.address_exclude(exclude_net)) for r in remaining: print(r) # 10.0.0.0/26 # 10.0.0.128/25

四、网络操作

ipaddress 模块提供了简洁的语法来执行常见的网络操作,包括包含关系判断、子网划分、网络重叠检查等。这些操作在网络安全、网络规划、路由分析等场景中频繁使用。

包含判断

使用 in 运算符可以判断一个地址是否属于某个网络,或者一个网络是否是另一个网络的子网。这种直观的语法使得网络包含关系判断代码非常简洁易读。

net = ipaddress.IPv4Network('192.168.1.0/24') # 地址是否在网络中 print(ipaddress.IPv4Address('192.168.1.50') in net) # True print(ipaddress.IPv4Address('192.168.2.1') in net) # False # 网络是否包含另一个网络 sub_net = ipaddress.IPv4Network('192.168.1.0/25') print(sub_net in net) # True(/25是/24的子网) another = ipaddress.IPv4Network('10.0.0.0/8') print(another in net) # False # 边界地址判断 print(net.network_address in net) # True(网络地址自身) print(net.broadcast_address in net) # True(广播地址)

子网划分

子网划分是网络管理中最常见的操作之一。ipaddress 提供了灵活的子网划分方法,支持按前缀长度差或直接指定新前缀长度。此外,还可以使用 subnets_of() 方法来判断一个网络是否可以被另一个网络包含。

# 将/16网络划分为/20子网 net = ipaddress.IPv4Network('172.16.0.0/16') subnets = list(net.subnets(new_prefix=20)) print(f"子网数量: {len(subnets)}") # 16(2^(20-16) = 16) print(subnets[0]) # 172.16.0.0/20 print(subnets[1]) # 172.16.16.0/20 print(subnets[-1]) # 172.16.240.0/20 # subnet_of() 判断子网关系 parent = ipaddress.IPv4Network('10.0.0.0/16') child = ipaddress.IPv4Network('10.0.1.0/24') other = ipaddress.IPv4Network('10.1.0.0/24') print(child.subnet_of(parent)) # True print(other.subnet_of(parent)) # False print(parent.supernet_of(child)) # True

网络交集与重叠检查

overlaps() 方法用于检查两个网络是否存在重叠(即共享部分地址空间)。这在网络规划中避免地址冲突、合并路由条目时非常有用。

# 检查网络是否重叠 net1 = ipaddress.IPv4Network('10.0.0.0/24') net2 = ipaddress.IPv4Network('10.0.0.128/25') # net1 的子网 net3 = ipaddress.IPv4Network('10.0.1.0/24') # 邻近网络 print(net1.overlaps(net2)) # True(net2 是 net1 的子网,完全重叠) print(net1.overlaps(net3)) # False(两个网络不重叠) # 部分重叠的情况 net4 = ipaddress.IPv4Network('10.0.0.128/25') net5 = ipaddress.IPv4Network('10.0.0.0/25') net6 = ipaddress.IPv4Network('10.0.0.64/26') # 跨越 net4 和 net5 的边界 # 相邻不重叠的情况: print(net4.overlaps(net5)) # False(net4是128-255,net5是0-127,不重叠) # 通过地址范围计算重叠 def check_overlap(net_a, net_b): a_start = int(net_a.network_address) a_end = int(net_a.broadcast_address) b_start = int(net_b.network_address) b_end = int(net_b.broadcast_address) return a_start <= b_end and b_start <= a_end print(check_overlap(net4, net5)) # False print(check_overlap(net4, net6)) # True

五、实战应用

理论知识的最终目的是解决实际问题。本节展示 ipaddress 模块在真实网络编程中的三个典型应用场景:IP地址验证、子网扫描和CIDR合并。

应用一:IP地址验证函数

在Web应用或网络工具开发中,验证用户输入的IP地址或CIDR格式是否正确是常见需求。ipaddress 模块通过异常机制提供了天然的验证能力。

def validate_ip(address): """验证IP地址格式是否正确,支持IPv4和IPv6。返回 (是否有效, 对象或错误信息)""" try: addr = ipaddress.ip_address(address) return True, addr except ValueError as e: return False, str(e) def validate_network(cidr, strict=True): """验证CIDR网络格式是否正确""" try: net = ipaddress.ip_network(cidr, strict=strict) return True, net except ValueError as e: return False, str(e) def classify_address(address): """分类IP地址并提供详细信息""" addr = ipaddress.ip_address(address) info = { 'address': str(addr), 'version': addr.version, 'is_private': addr.is_private, 'is_global': addr.is_global, 'is_loopback': addr.is_loopback, 'is_multicast': addr.is_multicast, 'is_link_local': addr.is_link_local, } return info # 测试 print(validate_ip('192.168.1.1')) # (True, IPv4Address('192.168.1.1')) print(validate_ip('256.1.1.1')) # (False, '... 256 ...') print(validate_ip('::1')) # (True, IPv6Address('::1')) print(classify_address('127.0.0.1')) # {'address': '127.0.0.1', 'version': 4, 'is_private': False, # 'is_global': False, 'is_loopback': True, ...}

应用二:子网扫描

在网络运维中,经常需要扫描某个子网内的所有地址,或者对特定范围内的地址进行操作。结合 hosts() 方法和 subnets() 方法,可以灵活地实现各种扫描策略。

def scan_subnet(cidr): """扫描子网内所有可用主机地址,返回地址列表""" net = ipaddress.ip_network(cidr) return list(net.hosts()) def get_network_info(cidr): """获取网络的完整信息摘要""" net = ipaddress.ip_network(cidr) return { 'network': str(net), 'netmask': str(net.netmask), 'broadcast': str(net.broadcast_address), 'total_addresses': net.num_addresses, 'usable_hosts': net.num_addresses - 2 if net.num_addresses > 2 else net.num_addresses, 'prefixlen': net.prefixlen, 'version': net.version, } # 划分多个小网络并分别扫描 def multi_subnet_scan(cidr, sub_prefix): net = ipaddress.ip_network(cidr) results = {} for sub in net.subnets(new_prefix=sub_prefix): results[str(sub)] = { 'hosts': list(sub.hosts()), 'total': sub.num_addresses, } return results # 示例:将 /24 划分为 4 个 /26,分别获取信息 info = multi_subnet_scan('10.0.0.0/24', 26) for net_name, data in info.items(): print(f"{net_name}: {data['total']} 个地址")

应用三:CIDR合并

在路由表优化和防火墙规则聚合中,经常需要将多个连续的CIDR块合并为更少、更大的网络块。collapse_addresses() 函数提供了这个能力。

def merge_cidrs(cidr_list): """将多个CIDR块合并为最小的不重叠网络列表""" networks = [ipaddress.ip_network(c) for c in cidr_list] merged = ipaddress.collapse_addresses(networks) return list(merged) # 合并连续子网 cidrs = [ '192.168.0.0/24', '192.168.1.0/24', '192.168.2.0/24', '192.168.3.0/24', ] result = merge_cidrs(cidrs) for r in result: print(r) # 192.168.0.0/22(4个/24被合并为一个/22) # 非连续子网无法合并 cidrs2 = ['10.0.0.0/24', '10.0.2.0/24'] result2 = merge_cidrs(cidrs2) for r in result2: print(r) # 10.0.0.0/24(无法合并,各自保留) # 10.0.2.0/24 # 混合大小的子网合并 cidrs3 = [ '10.1.0.0/24', '10.1.1.0/24', '10.1.2.0/23', '10.1.4.0/24', ] result3 = merge_cidrs(cidrs3) for r in result3: print(r) # 10.1.0.0/22(前三个合并) # 10.1.4.0/24(单独)

扩展应用:除了 collapse_addresses(),ipaddress 模块还提供了 summarize_address_range(first, last) 函数,用于将任意连续的地址范围转换为最简CIDR列表。这在防火墙规则优化和IP地址分配审计中非常实用。

# summarize_address_range:将地址范围转为CIDR列表 first = ipaddress.IPv4Address('192.168.0.0') last = ipaddress.IPv4Address('192.168.3.255') summary = list(ipaddress.summarize_address_range(first, last)) for s in summary: print(s) # 192.168.0.0/22 # 非对齐范围也能正确处理 first2 = ipaddress.IPv4Address('10.0.0.100') last2 = ipaddress.IPv4Address('10.0.0.200') summary2 = list(ipaddress.summarize_address_range(first2, last2)) for s in summary2: print(s) # 10.0.0.100/32 # 10.0.0.101/32 # 10.0.0.102/31 # ...(自动生成最简CIDR表示)

六、核心总结

ipaddress 模块核心要点:

1. 两大核心类:IPv4Address/IPv6Address 表示单个IP地址,IPv4Network/IPv6Network 表示子网。工厂函数 ip_address()ip_network() 自动识别协议版本。

2. 地址属性丰富:通过 is_privateis_globalis_multicastis_loopbackis_link_local 等布尔属性快速判断地址类型。

3. 网络对象核心:network_addressbroadcast_addressnetmaskhostmasknum_addresses 提供网络边界信息。

4. 子网划分:subnets(prefixlen_diff) / subnets(new_prefix=N) 将网络划分为更小子网;supernet() 向上合并。

5. 包含与重叠:in 运算符判断地址/网络的包含关系;overlaps() 检查网络是否重叠。

6. 实用工具函数:collapse_addresses() 合并CIDR;summarize_address_range() 将地址范围转为CIDR;hosts() 遍历可用主机地址;address_exclude() 排除子网。

7. 严格模式:默认严格模式要求构造网络时传入的地址必须是网络地址(主机位全零),可通过 strict=False 放宽。

8. 不可变性:所有 IP 地址和网络对象都是不可变的,创建后不能修改,保证了线程安全。

ipaddress 模块设计简洁、接口统一,是Python标准库中处理网络地址问题的首选工具。无论是简单的地址验证,还是复杂的子网规划与CIDR合并,它都能以寥寥数行代码优雅地解决问题。熟练掌握该模块,对于网络编程、系统管理脚本、安全工具开发等领域都有着直接的帮助。