From 045d3cc5ca7f1a646e3dc526fe01bd123ba63864 Mon Sep 17 00:00:00 2001 From: Lukasz Forynski Date: Sun, 1 Sep 2013 01:38:30 +0100 Subject: [PATCH] updated __setitem__ to allow for update items using multiple keys --- multi_key_dict/README.txt | 29 +++++++++++++++---- multi_key_dict/multi_key_dict.py | 49 ++++++++++++++++++++++++++++---- 2 files changed, 68 insertions(+), 10 deletions(-) diff --git a/multi_key_dict/README.txt b/multi_key_dict/README.txt index f6cce64..fb96862 100644 --- a/multi_key_dict/README.txt +++ b/multi_key_dict/README.txt @@ -2,14 +2,33 @@ multi_key_dict ====================== -Implementation of a multi-key dictionary. +Implementation of a multi-key dictionary, i.e.: -This kind of dictionary has a similar interface to the standard dictionary, +(key1[,key2, ..]) => value -and indeed if used with single key key elements - it's behaviour is the same as for a standard dict. +This dictionary has a similar interface to the standard dictionary => but is extended to support multiple keys referring to the same element. + +If element is created using multiple keys, e.g.: + +.. code:: python + + from multi_key_dict import multi_key_dict + + k = multi_key_dict() + k[1000, 'kilo', 'k'] = 'kilo (x1000)' + + print k[1000] # will print 'kilo (x1000)' + print k['k'] # will also print 'kilo (x1000)' + + # the same way objects can be updated, deleted: + # and if an object is updated using one key, the new value will + # be accessible using any other key, e.g. for example above: + k['kilo'] = 'kilo' + print k[1000] # will now print 'kilo' as value was updated + +These elements can be accessed using either of those keys (e.g for read/update/deletion). -However it also allows for creation of elements using multiple keys (using tuples/lists). Such elements can be accessed using either of those keys (e.g for read/update/deletion). Multi-key dict provides also extended interface for iterating over items and keys (e.g. by the key type), which might be useful when creating, e.g. dictionaries with index-name key pair allowing to iterate over items using either: names or indexes. It can be useful for many many other similar use-cases, and there is no limit to the number of keys used to map to the value. -There are also methods to get other keys that map to the same element and others. Refer to examples and test code to see it in action. +There are few other useful methods, e.g. to iterate over dictionary (by/using) selected key type, finding other keys mapping to the same value etc. Refer to example/test code to see it in action. \ No newline at end of file diff --git a/multi_key_dict/multi_key_dict.py b/multi_key_dict/multi_key_dict.py index 1f2b12b..3113db8 100644 --- a/multi_key_dict/multi_key_dict.py +++ b/multi_key_dict/multi_key_dict.py @@ -50,7 +50,7 @@ class multi_key_dict(object): # below creates entry with two possible key types: int and str, # mapping all keys to the assigned value k[1000, 'kilo', 'k'] = 'kilo (x1000)' - print k[100] # will print 'kilo (x1000)' + print k[1000] # will print 'kilo (x1000)' print k['k'] # will also print 'kilo (x1000)' # the same way objects can be updated, and if an object is updated using one key, the new value will @@ -67,12 +67,32 @@ class multi_key_dict(object): def __setitem__(self, keys, value): """ Set the value at index (or list of indexes) specified as keys. - Note, that if multiple key list is specified - none of them can exist already - in this dictionary. If it does exist, KeyError is raised. """ - if(type(keys) in [tuple, list]): + Note, that if multiple key list is specified, either: + - none of keys should map to an existing item already (item creation), or + - all of keys should map to exactly the same item (as previously created) + (item update) + If this is not the case - KeyError is raised. """ + if(type(keys) in [tuple, list]): num_of_keys_we_have = reduce(lambda x, y: x+y, map(lambda x : self.has_key(x), keys)) if num_of_keys_we_have: - raise KeyError(', '.join(str(key) for key in keys)) + all_select_same_item = True + for key in keys: + direct_key = None + try: + if direct_key is None: + direct_key = self.items_dict[key] + else: + new = self.items_dict[key] + if new != direct_key: + all_select_same_item = False + break + except: + all_select_same_item = False + break; + + if num_of_keys_we_have < len(keys) and not all_select_same_item: + raise KeyError(', '.join(str(key) for key in keys)) + first_key = keys[0] # combination if keys is allowed, simply use the first one else: first_key = keys @@ -456,6 +476,25 @@ def test_multi_key_dict(): # test keys() assert (m.keys(int) == tst_range), 'm.keys(int) is not as expected.' + + # test setitem with multiple keys + m['xy', 999, 'abcd'] = 'teststr' + try: + m['xy', 998] = 'otherstr' + assert(False), 'creating / updating m[\'xy\', 998] should fail!' + except KeyError, err: + pass + + m['xy', 999] = 'otherstr' + assert (m['xy'] == 'otherstr'), 'm[\'xy\'] is not as expected.' + assert (m[999] == 'otherstr'), 'm[999] is not as expected.' + assert (m['abcd'] == 'otherstr'), 'm[\'abcd\'] is not as expected.' + + m['abcd', 'xy'] = 'another' + assert (m['xy'] == 'another'), 'm[\'xy\'] is not == \'another\'.' + assert (m[999] == 'another'), 'm[999] is not == \'another\'' + assert (m['abcd'] == 'another'), 'm[\'abcd\'] is not == \'another\'.' + print 'All test passed OK!'