Python для IP-адресов: обзор и практика
Основы работы с IP-адресами в Python
Как создать объект IP-адреса или подсети и выполнять с ними операции?
Встроенный модуль ipaddress предоставляет классы IPv4Address, IPv6Address, IPv4Network, IPv6Network, IPv4Interface, IPv6Interface. Это самое эффективное решение для большинства задач.
import ipaddress
# Создание IP-адреса
ip = ipaddress.IPv4Address('192.168.1.1')
print(ip) # 192.168.1.1
# Создание сети
net = ipaddress.IPv4Network('192.168.1.0/24', strict=False)
print(net) # 192.168.1.0/24
# Проверка принадлежности адреса сети
print(ip in net) # True
# Перебор всех хостов сети
for host in net.hosts():
print(host)
break # первый хост 192.168.1.1Python desktop app (создание десктопного приложения на python)
Возможные проблемы:
- При создании сети с
strict=True(по умолчанию) адрес сети должен быть сетевым адресом (все биты хостовой части равны 0). Если передан широковещательный адрес или адрес хоста, возникнетValueError. Используйтеstrict=Falseдля автоматического приведения. - IPv6 требует аналогичных классов
IPv6AddressиIPv6Network. Несовместимость версий приводит к ошибкам при сравнении. - Для работы с масками в нотации CIDR или dotted decimal используйте
net.netmaskиnet.prefixlen.
Как проверить, что IP-адрес входит в подсеть?
net = ipaddress.IPv4Network('10.0.0.0/8')
ip = ipaddress.IPv4Address('10.20.30.40')
if ip in net:
print('Адрес принадлежит сети')
# Trueигры на языке python (разработка игр на python)
Как получить список всех доступных хостов, исключая сетевой и широковещательный адреса?
net = ipaddress.IPv4Network('192.168.1.0/28')
for ip in net.hosts():
print(ip)
# Вывод: 192.168.1.1 ... 192.168.1.14
Python ip address (работа с ip-адресами в python)
Как преобразовать IP-адрес в целое число и обратно (модуль socket)?
Иногда требуется работать с IP как с 32-битным числом (для вычислений, хранения в БД). Модули socket и struct позволяют это сделать.
import socket
import struct
# IP в число (inet_aton для IPv4)
ip_str = '192.168.1.1'
ip_int = struct.unpack('!I', socket.inet_aton(ip_str))[0]
print(ip_int) # 3232235777
# Обратно
ip_back = socket.inet_ntoa(struct.pack('!I', ip_int))
print(ip_back) # 192.168.1.1Проблемы:
inet_atonне поддерживает IPv6. Для IPv6 используйтеinet_ptonс AF_INET6.- Для адресов с некорректным форматом (например, '192.168.1') возникнет
OSError. - Числовое представление не учитывает маску сети, только адрес.
Как проверить корректность IP-адреса без сторонних библиотек?
Ручной парсинг строки с разделением по точке и проверкой каждого октета (0-255) – простой, но неполный метод.
def is_valid_ipv4(ip):
parts = ip.split('.')
if len(parts) != 4:
return False
for part in parts:
try:
num = int(part)
if num < 0 or num > 255:
return False
except ValueError:
return False
return True
print(is_valid_ipv4('192.168.1.1')) # True
print(is_valid_ipv4('256.0.0.1')) # FalseПроблемы:
- Не проверяет ведущие нули (октет '01' пропускается, хотя некоторые системы считают его восьмеричным).
- Не обрабатывает IPv6.
- Не отделяет адрес от маски/порта.
Для серьёзной валидации лучше применять ipaddress.
Какие дополнительные возможности предоставляет библиотека netaddr?
Сторонняя библиотека netaddr (установка: pip install netaddr) предлагает более гибкие операции: работа с диапазонами, агрегация, пересечение сетей, случайные адреса и другое.
from netaddr import IPAddress, IPNetwork, cidr_merge
ip = IPAddress('10.0.0.1')
net = IPNetwork('10.0.0.0/24')
print(ip in net) # True
# Объединение нескольких подсетей в минимальный набор
nets = [IPNetwork('10.0.0.0/24'), IPNetwork('10.0.1.0/24')]
merged = cidr_merge(nets)
for m in merged:
print(m) # 10.0.0.0/23Проблемы:
- Не встроена в стандартную библиотеку, требуется установка.
- Может конфликтовать с именем модуля
ipaddressпри импорте (используйтеimport netaddr). - Обновлённая версия может изменить API.
Расширенные примеры работы с IP-адресами
Работа с масками и подсетями через ipaddress
import ipaddress
# Создание сети с маской
net = ipaddress.IPv4Network('192.168.1.0/24')
print('Маска:', net.netmask) # 255.255.255.0
print('Префикс:', net.prefixlen) # 24
print('Сетевой адрес:', net.network_address) # 192.168.1.0
print('Широковещательный адрес:', net.broadcast_address) # 192.168.1.255
# Разделение сети на подсети
subnets = list(net.subnets(prefixlen_diff=2)) # /26
for subnet in subnets:
print(subnet)
# 192.168.1.0/26
# 192.168.1.64/26
# 192.168.1.128/26
# 192.168.1.192/26
# Проверка пересечения сетей
net1 = ipaddress.IPv4Network('10.0.0.0/16')
net2 = ipaddress.IPv4Network('10.0.1.0/24')
print(net1.overlaps(net2)) # TrueРабота с IPv6
v6 = ipaddress.IPv6Address('2001:db8::1')
net6 = ipaddress.IPv6Network('2001:db8::/32')
print(v6 in net6) # True
print('Список хостов (первые 3):')
for i, host in enumerate(net6.hosts()):
if i >= 3:
break
print(host)
# 2001:db8::1
# 2001:db8::2
# 2001:db8::3Использование netaddr для расширенной обработки
from netaddr import *
from random import randint
# Диапазон IP
ip_range = IPRange('192.168.1.10', '192.168.1.20')
print('Количество адресов:', len(ip_range)) # 11
# Случайный IP из диапазона
random_ip = ip_range[randint(0, len(ip_range)-1)]
print('Случайный IP:', random_ip)
# Агрегация подсетей
nets = [IPNetwork('10.10.0.0/24'), IPNetwork('10.10.1.0/24'), IPNetwork('10.10.2.0/24')]
aggregated = cidr_merge(nets)
print('После агрегации:')
for n in aggregated:
print(n)
# 10.10.0.0/23
# 10.10.2.0/24 (не объединяется, так как разрыв)
# Преобразование IPv4 в число и обратно
ip = IPAddress('192.168.1.1')
print(int(ip)) # 3232235777
print(IPAddress(3232235777)) # 192.168.1.1Сравнение производительности методов
import time
import ipaddress
# Проверка вхождения 100000 адресов
net = ipaddress.IPv4Network('10.0.0.0/8')
start = time.perf_counter()
for i in range(100000):
ip = ipaddress.IPv4Address('10.0.0.%d' % (i % 255))
_ = ip in net
print('ipaddress:', time.perf_counter() - start)
# Сравнение с ручным разбором
def manual_check(ip_str):
# упрощённая проверка, в реальности сложнее
parts = ip_str.split('.')
first = int(parts[0])
return first == 10
start = time.perf_counter()
for i in range(100000):
_ = manual_check('10.0.0.%d' % (i % 255))
print('manual:', time.perf_counter() - start)
# Вывод: ipaddress медленнее, но надёжнее.