Q:

Why do dicts of defaultdict(int)'s use so much memory? ( and other simple python performance questions)

I do understand that querying a non-existent key in a defaultdict the way I do will add items to the defaultdict.  That is why it is fair to compare my 2nd code snippet to my first one in terms of performance. 
import numpy as num
from collections import defaultdict

topKeys = range(16384)
keys = range(8192)

table = dict((k,defaultdict(int)) for k in topKeys)

dat = num.zeros((16384,8192), dtype="int32")

print "looping begins"
#how much memory should this use?  I think it shouldn't use more that a few
#times the memory required to hold (16384*8192) int32's (512 mb), but
#it uses 11 GB! 
for k in topKeys:
    for j in keys:
        dat[k,j] = table[k][j]

print "done"

What is going on here?  Furthermore, this similar script takes eons to run compared to the first one, and also uses an absurd quantity of memory. 
topKeys = range(16384)
keys = range(8192)
table = [(j,0) for k in topKeys for j in keys]

I guess python ints might be 64 bit ints, which would account for some of this, but do these relatively natural and simple constructions really produce such a massive overhead? 
I guess these scripts show that they do, so my question is: what exactly is causing the high memory usage in the first script and the long runtime and high memory usage of the second script and is there any way to avoid these costs? 
Edit:
Python 2.6.4 on 64 bit machine. 
Edit 2:  I can see why, to a first approximation, my table should take up 3 GB
16384*8192*(12+12) bytes
 and 6GB with a defaultdict load factor that forces it to reserve double the space. 
Then inefficiencies in memory allocation eat up another factor of 2. 
So here are my remaining questions:
Is there a way for me to tell it to use 32 bit ints somehow?   
And why does my second code snippet take FOREVER to run compared to the first one?   The first one takes about a minute and I killed the second one after 80 minutes. 

A:

Python ints are internally represented as C longs (it's actually a bit more complicated than that), but that's not really the root of your problem. 
The biggest overhead is your usage of dicts. ( defaultdicts and dicts are about the same in this description).  dicts are implemented using hash tables, which is nice because it gives quick lookup of pretty general keys. ( It's not so necessary when you only need to look up sequential numerical keys, since they can be laid out in an easy way to get to them.) 
A dict can have many more slots than it has items.  Let's say you have a dict with 3x as many slots as items.  Each of these slots needs room for a pointer to a key and a pointer serving as the end of a linked list.  That's 6x as many points as numbers, plus all the pointers to the items you're interested in.  Consider that each of these pointers is 8 bytes on your system and that you have 16384 defaultdicts in this situation.  As a rough, handwavey look at this, 16384 occurrences * (8192 items/occurance) * 7 (pointers/item) * 8 (bytes/pointer) = 7 GB.  This is before I've gotten to the actual numbers you're storing (each unique number of which is itself a Python dict), the outer dict, that numpy array, or the stuff Python's keeping track of to try to optimize some.  
Your overhead sounds a little higher than I suspect and I would be interested in knowing whether that 11GB was for a whole process or whether you calculated it for just table.  In any event, I do expect the size of this dict-of-defaultdicts data structure to be orders of magnitude bigger than the numpy array representation. 
As to "is there any way to avoid these costs?"  the answer is "use numpy for storing large, fixed-size contiguous numerical arrays, not dicts!"  You'll have to be more specific and concrete about why you found such a structure necessary for better advice about what the best solution is.