Python
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
265: The ipaddress Module for Network Addresses
A few years ago, I was reviewing a PR from a developer who was building a custom firewall rule validator. He had written about fifty lines of code using regular expressions and complex bit-shifting logic just to determine if a given IP address belonged to a specific CIDR subnet. He was almost there, but he had a bug where he forgot to account for the network and broadcast addresses, leading to a few "off-by-one" errors that would have been a nightmare to debug in production. I pointed him toward the ipaddress module, and we replaced those fifty lines with about three. It's a perfect example of why you shouldn't try to do "string math" on network addresses.
Handling Individual Addresses and Versions
The ipaddress module treats IP addresses as first-class objects rather than just strings. This is huge because it allows you to perform logical comparisons and validations without worrying about whether the input is IPv4 or IPv6. When you use ipaddress.ip_address(), Python automatically detects the version for you.
import ipaddress
# This could be an IPv4 or IPv6 string
addr_str = "192.168.1.15"
addr = ipaddress.ip_address(addr_str)
print(f"Version: {addr.version}") # Output: Version: 4
print(f"Is private? {addr.is_private}") # Output: Is private? True
# It works exactly the same for IPv6
ipv6_addr = ipaddress.ip_address("2001:db8::")
print(f"Version: {ipv6_addr.version}") # Output: Version: 6
I find the is_private property especially useful when building internal tools; it's a quick way to verify if an address is within the RFC 1918 range without having to memorize those ranges yourself.
Navigating Subnets and CIDR Ranges
Dealing with subnets is where this module really shines. By using ipaddress.ip_network(), you create an object that represents an entire block of addresses. You can then check if a specific address is part of that block using the in operator, which is incredibly intuitive.
# Define a subnet
network = ipaddress.ip_network("192.168.1.0/24")
# Check if an IP is inside that subnet
ip = ipaddress.ip_address("192.168.1.50")
if ip in network:
print("Access granted: IP is in the subnet")
# You can even iterate over all usable hosts in the network
# This excludes the network and broadcast addresses automatically
for host in network.hosts():
# Be careful doing this with a /8 network unless you want your loop to run forever
if host.compressed == "192.168.1.1":
print("Found the gateway!")
break
One thing to watch out for: if you try to create a network using an address that isn't the actual network address (e.g., ip_network("192.168.1.50/24")), Python will raise a ValueError because the host bits are set. If you don't care about that and just want the network the IP belongs to, pass strict=False.
The Distinction Between Interfaces and Networks
You'll occasionally run into a situation where you have an IP address and a subnet mask together, representing a specific device's configuration. This is an "interface," not a "network." While ip_network describes the whole neighborhood, ip_interface describes a specific house and the neighborhood it lives in.
# An interface is an IP and a mask combined
iface = ipaddress.ip_interface("192.168.1.50/24")
print(f"IP: {iface.ip}") # 192.168.1.50
print(f"Network: {iface.network}") # 192.168.1.0/24
print(f"Netmask: {iface.netmask}") # 255.255.255.0
Using the correct object—ip_address, ip_network, or ip_interface—makes your intent clear to anyone else reading your code. It transforms network logic from "string manipulation" into "object interaction," which is exactly how you want to be thinking in Python.
📋 Practical Task
Build a Network Access Control List (ACL) Filter
You are tasked with creating a security script that filters a list of incoming connection attempts. You have a list of "Allowed Subnets" and a list of "Incoming IPs." Your goal is to create a function that returns only the IPs that are permitted based on the allowed subnets.
Requirements:
- Define a list of allowed CIDR networks:
["10.0.0.0/8", "192.168.1.0/24", "2001:db8::/32"]. - Define a list of incoming IP strings:
["10.5.0.1", "192.168.1.15", "172.16.0.5", "2001:db8::1", "8.8.8.8"]. - Write a function
filter_allowed_ips(incoming_ips, allowed_networks)that:- Converts the subnet strings into
ip_networkobjects. - Converts each incoming IP string into an
ip_addressobject. - Returns a list of IPs (as strings) that belong to at least one of the allowed networks.
- Converts the subnet strings into
# Expected Output for the data above:
# ['10.5.0.1', '192.168.1.15', '2001:db8::1']
There are no comments for now.