Apart from 2d_dict
being an invalid variable name (it starts with a digit), your existing solution already works:
>>> from collections import defaultdict
>>> d2_dict = defaultdict(dict)
>>> d2_dict[('canned_food', 'food')]['spam'] = 'delicious'
>>> d2_dict
defaultdict(<type 'dict'>, {('canned_food', 'food'): {'spam': 'delicious'}})
In fact, you don't even need the parentheses - Python will still recognise your key as a tuple:
>>> d2_dict['fresh_food', 'food']['eggs'] = 'delicious'
>>> d2_dict
defaultdict(<type 'dict'>, {('canned_food', 'food'): {'spam': 'delicious'},
('fresh_food', 'food'): {'eggs': 'delicious'}})
... and, yes, it's a perfectly reasonable way to build a 2D+1D lookup table.
If you want to build a 3D lookup table using nested dicts instead of tuple keys, this works:
>>> d3_dict = defaultdict(lambda: defaultdict(dict))
>>> d3_dict['dried_food']['food']['jerky'] = 'chewy'
>>> d3_dict
defaultdict(<function <lambda> at 0x7f20af38a2a8>,
{'dried_food': defaultdict(<type 'dict'>, {'food': {'jerky': 'chewy'}})})