The Python ipaddress module: subnets, hosts and validation
In short: The ipaddress module is part of the standard library and understands IPv4 and IPv6. It validates addresses, works out network and broadcast addresses, splits blocks into subnets and checks membership. Use it instead of writing your own string handling for IP addresses.
Why not just use strings?
An IP address looks like text, but treating it as text leads to bugs. String comparison says "10.0.0.10" sorts before "10.0.0.9", and a home-made regular expression will happily accept 300.1.1.1. Python’s built-in ipaddress module understands addresses as numbers with rules, for both IPv4 and IPv6, and needs no installation.
Addresses, networks and interfaces
ip_address(): one address, such as a host or gateway.ip_network(): a subnet, such as 192.168.10.0/24. By default it must be the network address; passstrict=Falseto accept a host address and mask.ip_interface(): an address plus its mask, as configured on an interface.
import ipaddress
net = ipaddress.ip_network("192.168.10.0/24")
print(net.network_address, net.broadcast_address, net.netmask)
print(net.num_addresses - 2, "usable hosts")
intf = ipaddress.ip_interface("192.168.10.1/24")
print(intf.ip, intf.network)
addr = ipaddress.ip_address("192.168.10.50")
print(addr in net, addr.is_private)
v6 = ipaddress.ip_network("2001:db8:10::/64")
print(v6.num_addresses)
Subnetting and listing hosts
The subnets() method splits a block, either by a new prefix length or by how many bits to borrow. hosts() yields the usable addresses, skipping the network and broadcast addresses in IPv4. It is a generator, so it does not build a huge list unless you ask it to.
import ipaddress
block = ipaddress.ip_network("10.20.0.0/22")
for sub in block.subnets(new_prefix=24):
hosts = list(sub.hosts())
print(sub, "gateway", hosts[0], "last", hosts[-1])
print(list(ipaddress.ip_network("10.20.0.0/30").hosts()))
a = ipaddress.ip_network("10.20.0.0/24")
b = ipaddress.ip_network("10.20.0.128/25")
print(b.subnet_of(a), a.overlaps(b))
Tip: check your answers against the subnet calculator and the VLSM tool while you learn, then let Python do the bulk work.
Validating input safely
Any address that comes from a person, a spreadsheet or an API should be parsed before you use it. Invalid input raises a ValueError, which you can catch and report clearly.
import ipaddress
def parse_host(text: str):
try:
return ipaddress.ip_address(text.strip())
except ValueError:
return None
for item in ["192.168.10.5", "10.1.1.300", "2001:db8::1", "server1"]:
result = parse_host(item)
print(item, "OK" if result else "invalid")
Validation is also a security control. If a script builds a command or a firewall rule from user input, checking the value with ipaddress stops odd strings from being passed through.
Practical uses at work
| Task | Useful feature |
|---|---|
| Plan VLAN subnets from a site block | subnets(new_prefix=...) |
| Check a new subnet does not clash | overlaps(), subnet_of() |
| Find which subnet a host is in | addr in net |
| Sort addresses correctly | sorted(..., key=ipaddress.ip_address) |
| Summarise contiguous ranges | collapse_addresses() |
Common mistakes include forgetting strict=False when parsing "192.168.10.1/24" as a network, and calling list() on hosts() for a very large IPv6 network, which would never finish.
Practice
What happens with ip_network("192.168.10.1/24")?
How many /26 subnets does a /24 split into?
Why sort addresses with key=ipaddress.ip_address rather than as strings?
Key takeaways
- ipaddress is built in and handles IPv4 and IPv6.
- ip_address, ip_network and ip_interface model hosts, subnets and interface addresses.
- subnets() splits blocks; hosts() yields usable addresses.
- Parse every external address and catch ValueError.
- Use overlaps() and subnet_of() to check address plans.
Original content written by Mohamed Fayas for FM TecHub. © 2026 FM TecHub. You're welcome to share a link to this page; please don't copy the text elsewhere without permission.