Issue
I want to make a 2d dictionary with multiple keys per value. I do not want to make a tuple a key. But rather make many keys that will return the same value.
I know how to make a 2d dictionary using defaultdict:
from collections import defaultdict
a_dict = defaultdict(dict)
a_dict['canned_food']['spam'] = 'delicious'
And I can make a tuple a key using
a_dict['food','canned_food']['spam'] = 'delicious'
But this does not allow me to do something like
print a_dict['canned_food']['spam']
Because 'canned_food' is not a key the tuple ['food','canned_food'] is the key.
I have learned that I can simply set many to same value independently like:
a_dict['food']['spam'] = 'delicious'
a_dict['canned_food']['spam'] = 'delicious'
But this becomes messy with a large number of keys. In the first dimension of dictionary I need ~25 keys per value. Is there a way to write the dictionary so that any key in the tuple will work?
I have asked this question before but was not clear on what I wanted so I am reposting. Thank you in advance for any help.
Solution
Here is a possible solution:
from collections import Iterable
class AliasDefaultDict():
def __init__(self, default_factory, initial=[]):
self.aliases = {}
self.data = {}
self.factory = default_factory
for aliases, value in initial:
self[aliases] = value
@staticmethod
def distinguish_keys(key):
if isinstance(key, Iterable) and not isinstance(key, str):
return set(key)
else:
return {key}
def __getitem__(self, key):
keys = self.distinguish_keys(key)
if keys & self.aliases.keys():
return self.data[self.aliases[keys.pop()]]
else:
value = self.factory()
self[keys] = value
return value
def __setitem__(self, key, value):
keys = self.distinguish_keys(key)
if keys & self.aliases.keys():
self.data[self.aliases[keys.pop()]] = value
else:
new_key = object()
self.data[new_key] = value
for key in keys:
self.aliases[key] = new_key
return value
def __repr__(self):
representation = defaultdict(list)
for alias, value in self.aliases.items():
representation[value].append(alias)
return "AliasDefaultDict({}, {})".format(repr(self.factory), repr([(aliases, self.data[value]) for value, aliases in representation.items()]))
Which can be used like so:
>>> a_dict = AliasDefaultDict(dict)
>>> a_dict['food', 'canned_food']['spam'] = 'delicious'
>>> a_dict['food']
{'spam': 'delicious'}
>>> a_dict['canned_food']
{'spam': 'delicious'}
>> a_dict
AliasDefaultDict(<class 'dict'>, [(['food', 'canned_food'], {'spam': 'delicious'})])
Note there are some edge cases with undefined behavior - such as using the same key for multiple aliases. I feel this makes this data type pretty awful for general use, and I'd suggest that you may be better off changing your program not to need this kind of overly convoluted structure instead.
Also note this solution is for 3.x, under 2.x, you will want to swap out str
for basestring
, and self.aliases.keys()
for self.aliases.viewkeys()
.
Answered By - Gareth Latty
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.