Skip to main content

Sorting a list of IP addresses in Python

As I work a lot with network data, one of my favourite python modules is iplib. It takes care of quite a few of things I want to do with IP addresses but lacks a lot of functionality of perl's Net::Netmask which I relied on extensively when perl was my favourite language.

One of the iplib missing features is a method for sorting a list of IP addresses, or at the very least, a method for comparing two addresses.

Luckily this is easy enough to implement yourself in python using a customised sort function. See the Sorting Mini-HOW TO for a well written document on sorting in python.

Here is my attempt at a custom function for sorting IP addresses.



import iplib

ips = ["192.168.100.56", "192.168.0.3", "192.0.0.192", "8.0.0.255"]

def ip_compare(x, y):
""" Compare two IP addresses. """
# Convert IP addresses to decimal for easy comparison
dec_x = int(iplib.convert(x, "dec"))
dec_y = int(iplib.convert(y, "dec"))
if dec_x > dec_y:
return 1
elif dec_x == dec_y:
return 0
else:
return -1


ips.sort(ip_compare) # Sort in place

print ips
['8.0.0.255', '192.0.0.192', '192.168.0.3', '192.168.100.56']

Sorted.

Update: A few people left comments which helped me realise how inefficient the above approach is. Take a look at the improved version.

Comments

Ben Beuchler said…
Wouldn't the obvious way to deal with this be to leave the IPs in decimal format and only convert them to dotted quads for display? Than all of the normal numeric comparisons, sorts, etc. would Just Work(tm).
Charles PiƱa said…
The cmp function in python will return 1, 0, -1 for >, =, < if those operands are defined for its comparators. A cleaner approach would be to replace your if/elif/else code with "return cmp(dec_x, dec_y)".
Tom Ritchford said…
Dreadful idea. Comparison functions get called rather a lot when you sort. You're redoing the conversion for both arguments EACH time you call the cmp function.

I agree with Ben above, just store these as ints and use the built-ins. That has the additional advantage that all of your code except the display areas is IP6 compatible.

If you simply can't do that, I'd suggest at least expanding your data structure to include the converted int IP address so you only have to make the conversion once per IP.

If you can't do that, at least be concise!

import iplib

def ip_to_decimal(x):
return int(iplib.convert(x, "dec"))

def ip_compare(x, y):
""" Compare two IP addresses. """
return cmp(ip_to_decimal(x), ip_to_decimal(y))

[on preview: Blogger totally sucks for code samples - there's no code tag and no pre tag - WTF is one supposed to do?!
logus said…
Don't over things. You can easily do it without defining your own comparison function. Just use `key` keyword argument:

ips.sort(key=lambda x: iplib.convert(x, 'dec'))

(I just like to read through documentation sometimes ;)

Popular posts from this blog

Normalizing a MAC address string

Over the last few days, I have been spending some time working on my python - reading the sections of Diving into Python that I have never got around to and refactoring parts of some of my python scripts to make better use of the features of language and, ultimately, to make them more robust (i.e. usable by people other than me). The script I have started with is a simple one for registering hosts for DHCP access. Basically, it takes two command line arguments - a fully qualified hostname and a MAC address - and then does some validation, checks that neither address is already in use, normalizes the output to the correct format, constructs a properly formatted host stanza and appends it to the end of our ISC DHCP servers dhcpd.conf configuration file. I have made improvements to various parts of the code but the changes I am most conflicted about are those I have made to the MAC address normalization function which works reliably and therefore probably isn't a good candidate for

More pyparsing and DHCP hosts

Since I wrote my original pyparsing post a few days ago, I've done some more work on refining my ISC dhcpd.conf host parsing example program. I also received some useful comments and suggests from Paul McGuire, the author the pyparsing module (thanks, Paul!), which I have also tried to incorporate. It's it's currently just a useless toy program but it is starting to look quite pretty. #!/usr/bin/python from pyparsing import * # An few host entries from dhcpd.conf sample_data = """ # A host with dynamic DNS attributes host a.foo.bar { ddns-hostname a; ddns-domainname "foo.bar"; hardware ethernet 00:11:22:33:44:55; fixed-address 192.168.100.10, 192.168.200.50; } # A simple multi-line host host b.foo.bar { hardware ethernet 00:0f:12:34:56:78; fixed-address 192.168.100.20; } # A simple single-line host host c.foo.bar { hardware ethernet 00:0e:12:34:50:70; fixed-address 192.168.100.40; } """ digits = "0123456789&qu