Getting clever with list comprehensions

Suppose you have a list of TCP connections:

packets = [
    { 'src': ('192.168.1.1', 22045), 'dst': ('192.168.1.3', 31037) },
    { 'src': ('192.168.1.1', 11567), 'dst': ('192.168.1.2', 28432) },
    { 'src': ('192.168.1.1', 22045), 'dst': ('192.168.1.3', 31037) },
    { 'src': ('192.168.1.7', 1), 'dst': ('192.168.1.8', 2) }
]

If we just want to get a quick idea of who is talking to who (most data of this sort would be much larger than the example above), a quick count of how many packets are in each conversation is a good start. Here’s the simple code to parse that out:

packet_count = {}
for p in packets:
    if (p['src'], p['dst']) in packet_count:
        packet_count[(p['src'], p['dst'])] += 1
    else:
        packet_count[(p['src'], p['dst'])] = 1
print packet_count

--- output ---
{(('192.168.1.1', 11567), ('192.168.1.2', 28432)): 1,
 (('192.168.1.1', 22045), ('192.168.1.3', 31037)): 2,
 (('192.168.1.7', 1), ('192.168.1.8', 2)): 1}

But we can simplify this code a lot just by using defaultdict:

from collections import defaultdict
packet_count = defaultdict(int)
for p in packets:
        packet_count[(p['src'], p['dst'])] += 1
print packet_count

--- output ---
defaultdict(<type 'int'>, 
    {(('192.168.1.7', 1), ('192.168.1.8', 2)): 1, 
    (('192.168.1.1', 22045), ('192.168.1.3', 31037)): 2, 
    (('192.168.1.1', 11567), ('192.168.1.2', 28432)): 1})

I’ve got a while until this plane lands, so how about a clever one-liner?

packet_count = dict([((p['src'],p['dst']), packets.count(p)) 
                    for p in packets])
print packet_count

--- output ---
{(('192.168.1.1', 11567), ('192.168.1.2', 28432)): 1,
 (('192.168.1.1', 22045), ('192.168.1.3', 31037)): 2,
 (('192.168.1.7', 1), ('192.168.1.8', 2)): 1}

But wait, there’s more! Python 2.7 supports dictionary comprehensions, allowing us to do…

packet_count = {(p['src'],p['dst']) : packets.count(p) 
                    for p in packets}
print packet_count

--- output ---
{(('192.168.1.1', 11567), ('192.168.1.2', 28432)): 1,
 (('192.168.1.1', 22045), ('192.168.1.3', 31037)): 2,
 (('192.168.1.7', 1), ('192.168.1.8', 2)): 1}