Last modified: Sep 13, 2026

Mastering IP Address Handling in Python

Working with IP addresses in Python can be challenging without the right tools. Fortunately, Python's ipaddress module simplifies this task significantly. Whether you're dealing with IPv4 or IPv6 addresses, this built-in module provides powerful functionality for parsing, analyzing, and manipulating IP data.

Introduction to the ipaddress Module

The ipaddress module was introduced in Python 3.3. It offers classes and functions to work with both IPv4 and IPv6 addresses. This module helps developers validate, compare, and manipulate IP addresses efficiently.

To start using the module, simply import it:


import ipaddress

Creating IP Address Objects

You can create IP address objects using the ip_address() function. This function automatically detects whether the address is IPv4 or IPv6.


import ipaddress

# Creating an IPv4 address
ipv4_addr = ipaddress.ip_address('192.168.1.1')
print(ipv4_addr)
print(type(ipv4_addr))

# Creating an IPv6 address
ipv6_addr = ipaddress.ip_address('2001:db8::1')
print(ipv6_addr)
print(type(ipv6_addr))

192.168.1.1

2001:db8::1

This approach makes it easy to handle both address types with a single interface. The ip_address() function throws a ValueError if the input is invalid.

Validating IP Addresses

One of the most common tasks when working with IP addresses is validation. The ip_address() function serves as an excellent validator. If the address is malformed, it raises an exception.


import ipaddress

def validate_ip(address):
    try:
        ipaddress.ip_address(address)
        return True
    except ValueError:
        return False

# Testing valid addresses
print(validate_ip('192.168.1.1'))     # Valid IPv4
print(validate_ip('2001:db8::1'))     # Valid IPv6

# Testing invalid addresses
print(validate_ip('999.999.999.999')) # Invalid IPv4
print(validate_ip('not_an_ip'))       # Invalid format

True
True
False
False

This method ensures that only properly formatted addresses pass through your application logic.

Working with IPv4Address and IPv6Address Classes

While ip_address() is convenient, you can also directly instantiate IPv4Address and IPv6Address classes for more explicit control.


import ipaddress

# Direct instantiation
ipv4 = ipaddress.IPv4Address('10.0.0.1')
ipv6 = ipaddress.IPv6Address('fe80::1')

print(f"IPv4: {ipv4}")
print(f"IPv6: {ipv6}")

# Accessing properties
print(f"IPv4 version: {ipv4.version}")
print(f"IPv6 version: {ipv6.version}")
print(f"IPv4 compressed: {ipv4.compressed}")

IPv4: 10.0.0.1
IPv6: fe80::1
IPv4 version: 4
IPv6 version: 6
IPv4 compressed: 10.0.0.1

These classes provide useful properties like version and compressed representation of addresses.

Comparing IP Addresses

IP address objects support comparison operations. You can check equality, less than, or greater than relationships between addresses.


import ipaddress

addr1 = ipaddress.ip_address('192.168.1.1')
addr2 = ipaddress.ip_address('192.168.1.2')
addr3 = ipaddress.ip_address('192.168.1.1')

print(addr1 == addr3)   # True - same addresses
print(addr1 < addr2)     # True - 1 is less than 2
print(addr1 != addr2)    # True - different addresses

True
True
True

These comparisons are particularly useful when sorting lists of IP addresses or checking for specific values in network ranges.

Understanding Network Addresses and Subnetting

Beyond individual addresses, the ipaddress module also handles network definitions. You can work with entire subnets using ip_network().


import ipaddress

# Creating a network
network = ipaddress.ip_network('192.168.1.0/24')

print(f"Network: {network}")
print(f"Netmask: {network.netmask}")
print(f"Broadcast: {network.broadcast_address}")
print(f"Prefix length: {network.prefixlen}")

# Checking if an address is in the network
addr = ipaddress.ip_address('192.168.1.100')
print(f"Is in network: {addr in network}")

Network: 192.168.1.0/24
Netmask: 255.255.255.0
Broadcast: 192.168.1.255
Prefix length: 24
Is in network: True

This functionality is essential for network administration tasks, firewall rule generation, and security auditing.

Iterating Over Network Addresses

You can iterate over all addresses within a network using a simple loop:


import ipaddress

network = ipaddress.ip_network('192.168.1.0/30')

# Iterating over all addresses in the network
for address in network:
    print(address)

# Counting addresses in a network
print(f"Total addresses: {network.num_addresses}")

192.168.1.0
192.168.1.1
192.168.1.2
192.168.1.3
Total addresses: 4

This feature is useful for scanning networks, generating IP lists, or implementing DHCP-like services.

Working with Host Addresses

To get only usable host addresses (excluding network and broadcast), use the hosts() method:


import ipaddress

network = ipaddress.ip_network('192.168.1.0/24')

# Getting only host addresses
host_list = list(network.hosts())
print(f"First 5 hosts: {host_list[:5]}")
print(f"Last 5 hosts: {host_list[-5:]}")
print(f"Total hosts: {len(host_list)}")

First 5 hosts: [IPv4Address('192.168.1.1'), IPv4Address('192.168.1.2'), IPv4Address('192.168.1.3'), IPv4Address('192.168.1.4'), IPv4Address('192.168.1.5')]
Last 5 hosts: [IPv4Address('192.168.1.251'), IPv4Address('192.168.1.252'), IPv4Address('192.168.1.253'), IPv4Address('192.168.1.254'), IPv4Address('192.168.1.255')]
Total hosts: 254

Note that in a /24 network, the first address (network) and last address (broadcast) are excluded from host addresses.

Handling Strict vs Non-Strict Networks

When creating networks, you can specify whether host bits should be zeroed out using the strict parameter:


import ipaddress

# Strict mode (default) - host bits must be zero
try:
    network1 = ipaddress.ip_network('192.168.1.1/24', strict=True)
except ValueError as e:
    print(f"Strict error: {e}")

# Non-strict mode - host bits are zeroed automatically
network2 = ipaddress.ip_network('192.168.1.1/24', strict=False)
print(f"Non-strict network: {network2}")

Strict error: 192.168.1.1/24 has host bits set
Non-strict network: 192.168.1.0/24

This distinction is crucial when processing user input or network configurations that might contain non-canonical representations.

Converting Between Integer and IP Address Formats

The ipaddress module allows conversion between integer representations and IP addresses:


import ipaddress

# Converting IP to integer
ipv4 = ipaddress.ip_address('192.168.1.1')
print(f"IPv4 as integer: {int(ipv4)}")

# Converting integer to IP
integer_value = 3232235777
ipv4_from_int = ipaddress.ip_address(integer_value)
print(f"Integer as IPv4: {ipv4_from_int}")

# Same for IPv6
ipv6 = ipaddress.ip_address('::1')
print(f"IPv6 as integer: {int(ipv6)}")

IPv4 as integer: 3232235777
Integer as IPv4: 192.168.1.1
IPv6 as integer: 1

This conversion capability is useful for database storage, bitwise operations, and low-level network programming.

Working with Network Interfaces

The module also supports interface addresses with prefix lengths using ip_interface():


import ipaddress

# Creating an interface
interface = ipaddress.ip_interface('192.168.1.1/24')

print(f"Interface IP: {interface.ip}")
print(f"Network: {interface.network}")
print(f"Prefix length: {interface.prefixlen}")

# For IPv6 interfaces
ipv6_interface = ipaddress.ip_interface('2001:db8::1/64')
print(f"IPv6 interface IP: {ipv6_interface.ip}")
print(f"IPv6 network: {ipv6_interface.network}")

Interface IP: 192.168.1.1
Network: 192.168.1.0/24
Prefix length: 24
IPv6 interface IP: 2001:db8::1
IPv6 network: 2001:db8::/64

This is particularly helpful when parsing configuration files or working with routing tables.

Advanced Features and Best Practices

The ipaddress module includes several advanced features that enhance its utility:


import ipaddress

# Checking if address is private, loopback, or reserved
private_addr = ipaddress.ip_address('192.168.1.1')
loopback_addr = ipaddress.ip_address('127.0.0.1')

print(f"Private: {private_addr.is_private}")
print(f"Loopback: {loopback_addr.is_loopback}")
print(f"Reserved: {private_addr.is_reserved}")

# Getting reverse DNS pointer
addr = ipaddress.ip_address('192.168.1.1')
print(f"Reverse DNS: {addr.reverse_pointer}")

# Supernetting and subnetting
network = ipaddress.ip_network('192.168.0.0/23')
subnets = list(network.subnets(new_prefix=24))
for subnet in subnets:
    print(f"Subnet: {subnet}")

Private: True
Loopback: True
Reserved: False
Reverse DNS: 1.1.168.192.in-addr.arpa
Subnet: 192.168.0.0/24
Subnet: 192.168.1.0/24

These features make the module invaluable for network automation, security tools, and system administration scripts.

Common Pitfalls and How to Avoid Them

While the ipaddress module is powerful, there are some common mistakes to avoid:


import ipaddress

# ❌ Wrong: Not handling exceptions
# ip = ipaddress.ip_address('invalid')  # Raises ValueError

# ✅ Correct: Always handle potential errors
try:
    ip = ipaddress.ip_address('invalid')
except ValueError as e:
    print(f"Error: {e}")

# ❌ Wrong: Mixing IPv4 and IPv6 comparisons
# ipv4 = ipaddress.ip_address('192.168.1.1')
# ipv6 = ipaddress.ip_address('::1')
# print(ipv4 == ipv6)  # Always False

# ✅ Correct: Check versions before comparison
ipv4 = ipaddress.ip_address('192.168.1.1')
ipv6 = ipaddress.ip_address('::1')
if ipv4.version == ipv6.version:
    print(ipv4 == ipv6)
else:
    print("Different IP versions cannot be compared")

Error: 'invalid' does not appear to be an IPv4 or IPv6 address
Different IP versions cannot be compared

Always wrap IP address parsing in try-except blocks and verify address families before comparisons.

Conclusion

Python's ipaddress module is an indispensable tool for any developer working with network applications. From basic validation to complex subnetting operations, it provides a comprehensive and intuitive API for handling IP addresses of all kinds.

By mastering functions like ip_address(), ip_network(), and ip_interface(), you can write robust network-aware applications that properly handle edge cases and various address formats. Remember to always validate input, handle exceptions gracefully, and consider both IPv4 and IPv6 when designing your solutions.

The examples shown in this article demonstrate just a fraction of what's possible with the ipaddress module. As you become more comfortable with its capabilities, you'll find it invaluable for everything from simple address validation to complex network analysis tasks.