Add element to dict but don't overwrite value when key exists - python

I want to add elements (key-value-pairs) to a dict. But I want to prevent overwriting existing values when the key exists.
But I don't want to do an if check. I would prefer an exception.
d = {}
d['a'] = 1
d['b'] = 2
d['a'] = 3 # <-- raise Exception! "KeyExistsException'
What I don't want
if not 'a' in d:
d['a'] = 3

You can subclass dict and in particular, override the __setitem__ method.
This sounds like what you want:
class SpecialDict(dict):
def __setitem__(self, key, value):
if not key in self:
super(SpecialDict, self).__setitem__(key, value)
else:
raise Exception("Cannot overwrite key!") # You can make your own type here if needed
x = SpecialDict()
x['a'] = 1
x['b'] = 2
x['a'] = 3 #raises Exception

Instead of subclassing dict as suggested by JacobIRR, you could also define a helper function for storing a key-value pair in a dict that throws an exception when the key already exists:
class KeyExistsException(Exception):
pass
def my_add(the_dict, the_key, the_value):
if the_key in the_dict:
raise KeyExistsException("value already exists")
the_dict[the_key] = the_value
d = {}
my_add(d, 'a', 1)
my_add(d, 'b', 2)
my_add(d, 'a', 3) # <-- raise Exception! "KeyExistsException'

This might work for your use. I am a noob so I may be overlooking something.
d = {}
try:
d['new_key'] # check if key exists.
except KeyError: # if not exist create.
d['new_key'] = 'value'
else: # else raise exception KeyExists.
raise Exception ('KeyExists')

Related

How to implement insert for OrderedDict in python 3

I want to insert an item into an OrderedDict at a certain position.
Using the gist of this SO answer i have the problem that it doesn't work on python 3.
This is the implementation used
from collections import OrderedDict
class ListDict(OrderedDict):
def __init__(self, *args, **kwargs):
super(ListDict, self).__init__(*args, **kwargs)
def __insertion(self, link_prev, key_value):
key, value = key_value
if link_prev[2] != key:
if key in self:
del self[key]
link_next = link_prev[1]
self._OrderedDict__map[key] = link_prev[1] = link_next[0] = [link_prev, link_next, key]
dict.__setitem__(self, key, value)
def insert_after(self, existing_key, key_value):
self.__insertion(self._OrderedDict__map[existing_key], key_value)
def insert_before(self, existing_key, key_value):
self.__insertion(self._OrderedDict__map[existing_key][0], key_value)
Using it like
ld = ListDict([(1,1), (2,2), (3,3)])
ld.insert_before(2, (1.5, 1.5))
gives
File "...", line 35, in insert_before
self.__insertion(self._OrderedDict__map[existing_key][0], key_value)
AttributeError: 'ListDict' object has no attribute '_OrderedDict__map'
It works with python 2.7. What is the reason that it fails in python 3?
Checking the source code of the OrderedDict implementation shows that self.__map is used instead of self._OrderedDict__map. Changing the code to the usage of self.__map gives
AttributeError: 'ListDict' object has no attribute '_ListDict__map'
How come? And how can i make this work in python 3? OrderedDict uses the internal __map attribute to store a doubly linked list. So how can i access this attribute properly?
I'm not sure you wouldn't be better served just keeping up with a separate list and dict in your code, but here is a stab at a pure Python implementation of such an object. This will be an order of magnitude slower than an actual OrderedDict in Python 3.5, which as I pointed out in my comment has been rewritten in C.
"""
A list/dict hybrid; like OrderedDict with insert_before and insert_after
"""
import collections.abc
class MutableOrderingDict(collections.abc.MutableMapping):
def __init__(self, iterable_or_mapping=None, **kw):
# This mimics dict's initialization and accepts the same arguments
# Of course, you have to pass an ordered iterable or mapping unless you
# want the order to be arbitrary. Garbage in, garbage out and all :)
self.__data = {}
self.__keys = []
if iterable_or_mapping is not None:
try:
iterable = iterable_or_mapping.items()
except AttributeError:
iterable = iterable_or_mapping
for key, value in iterable:
self.__keys.append(key)
self.__data[key] = value
for key, value in kw.items():
self.__keys.append(key)
self.__data[key] = value
def insert_before(self, key, new_key, value):
try:
self.__keys.insert(self.__keys.index(key), new_key)
except ValueError:
raise KeyError(key) from ValueError
else:
self.__data[new_key] = value
def insert_after(self, key, new_key, value):
try:
self.__keys.insert(self.__keys.index(key) + 1, new_key)
except ValueError:
raise KeyError(key) from ValueError
else:
self.__data[new_key] = value
def __getitem__(self, key):
return self.__data[key]
def __setitem__(self, key, value):
self.__keys.append(key)
self.__data[key] = value
def __delitem__(self, key):
del self.__data[key]
self.__keys.remove(key)
def __iter__(self):
return iter(self.__keys)
def __len__(self):
return len(self.__keys)
def __contains__(self, key):
return key in self.__keys
def __eq__(self, other):
try:
return (self.__data == dict(other.items()) and
self.__keys == list(other.keys()))
except AttributeError:
return False
def keys(self):
for key in self.__keys:
yield key
def items(self):
for key in self.__keys:
yield key, self.__data[key]
def values(self):
for key in self.__keys:
yield self.__data[key]
def get(self, key, default=None):
try:
return self.__data[key]
except KeyError:
return default
def pop(self, key, default=None):
value = self.get(key, default)
self.__delitem__(key)
return value
def popitem(self):
try:
return self.__data.pop(self.__keys.pop())
except IndexError:
raise KeyError('%s is empty' % self.__class__.__name__)
def clear(self):
self.__keys = []
self.__data = {}
def update(self, mapping):
for key, value in mapping.items():
self.__keys.append(key)
self.__data[key] = value
def setdefault(self, key, default):
try:
return self[key]
except KeyError:
self[key] = default
return self[key]
def __repr__(self):
return 'MutableOrderingDict(%s)' % ', '.join(('%r: %r' % (k, v)
for k, v in self.items()))
I ended up implementing the whole collections.abc.MutableMapping contract because none of the methods were very long, but you probably won't use all of them. In particular, __eq__ and popitem are a little arbitrary. I changed your signature on the insert_* methods to a 4-argument one that feels a little more natural to me. Final note: Only tested on Python 3.5. Certainly will not work on Python 2 without some (minor) changes.
Trying out the new dict object in 3.7 and thought I'd try to implement what Two-Bit Alchemist had done with his answer but just overriding the native dict class because in 3.7 dict's are ordered.
''' Script that extends python3.7 dictionary to include insert_before and insert_after methods. '''
from sys import exit as sExit
class MutableDict(dict):
''' Class that extends python3.7 dictionary to include insert_before and insert_after methods. '''
def insert_before(self, key, newKey, val):
''' Insert newKey:value into dict before key'''
try:
__keys = list(self.keys())
__vals = list(self.values())
insertAt = __keys.index(key)
__keys.insert(insertAt, newKey)
__vals.insert(insertAt, val)
self.clear()
self.update({x: __vals[i] for i, x in enumerate(__keys)})
except ValueError as e:
sExit(e)
def insert_after(self, key, newKey, val):
''' Insert newKey:value into dict after key'''
try:
__keys = list(self.keys())
__vals = list(self.values())
insertAt = __keys.index(key) + 1
if __keys[-1] != key:
__keys.insert(insertAt, newKey)
__vals.insert(insertAt, val)
self.clear()
self.update({x: __vals[i] for i, x in enumerate(__keys)})
else:
self.update({newKey: val})
except ValueError as e:
sExit(e)
A little testing:
In: v = MutableDict([('a', 1), ('b', 2), ('c', 3)])
Out: {'a': 1, 'b': 2, 'c': 3}
In: v.insert_before('a', 'g', 5)
Out: {'g': 5, 'a': 1, 'b': 2, 'c': 3}
In: v.insert_after('b', 't', 5)
Out: {'g': 5, 'a': 1, 'b': 2, 't': 5, 'c': 3}
Edit: I decided to do a little benchmark test to see what kind of performance hit this would take. I will use from timeit import timeit
Get a baseline. Create a dict with arbitrary values.
In: timeit('{x: ord(x) for x in string.ascii_lowercase[:27]}', setup='import string', number=1000000)
Out: 1.8214202160015702
See how much longer it would take to initialize the MutableDict with the same arbitrary values as before.
In: timeit('MD({x: ord(x) for x in string.ascii_lowercase[:27]})', setup='import string; from MutableDict import MutableDict as MD', number=1000000)
Out: 2.382507269998314
1.82 / 2.38 = 0.76. So if I'm thinking about this right MutableDict is 24% slower on creation.
Lets see how long it takes to do an insert. For this test I'll use the insert_after method as it is slightly bigger. Will also look for a key close to the end for insertion. 't' in this case.
In: timeit('v.insert_after("t", "zzrr", ord("z"))', setup='import string; from MutableDict import MutableDict as MD; v = MD({x: ord(x) for x in string.ascii_lowercase[:27]})' ,number=1000000)
Out: 3.9161406760104
2.38 / 3.91 = 0.60, 40% slower inserting_after than it's initialization. Not bad on a small test of 1 million loops. For a comparison in time relation we'll test this:
In: timeit('"-".join(map(str, range(100)))', number=1000000)
Out: 10.342204540997045
Not quite an apples to apples comparison but I hope these tests will aid you in your(reader not necessarily OP) decision to use or not use this class in your 3.7 projects.
Since Python 3.2, move_to_end can be used to move items around in an OrderedDict. The following code will implement the insert functionality by moving all items after the provided index to the end.
Note that this isn't very efficient and should be used sparingly (if at all).
def ordered_dict_insert(ordered_dict, index, key, value):
if key in ordered_dict:
raise KeyError("Key already exists")
if index < 0 or index > len(ordered_dict):
raise IndexError("Index out of range")
keys = list(ordered_dict.keys())[index:]
ordered_dict[key] = value
for k in keys:
ordered_dict.move_to_end(k)
There are obvious optimizations and improvements that could be made, but that's the general idea.
from collections import OrderedDict
od1 = OrderedDict([
('a', 1),
('b', 2),
('d', 4),
])
items = od1.items()
items.insert(2, ('c', 3))
od2 = OrderedDict(items)
print(od2) # OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])

How to prevent key creation through d[key] = val

Suppose I have d = {'dogs': 3}. Using:
d['cats'] = 2
would create the key 'cats' and give it the value 2.
If I really intend to update a dict with a new key and value, I would use d.update(cats=2) because it feels more explicit.
Having automatic creation of a key feels error prone (especially in larger programs), e.g.:
# I decide to make a change to my dict.
d = {'puppies': 4, 'big_dogs': 2}
# Lots and lots of code.
# ....
def change_my_dogs_to_maximum_room_capacity():
# But I forgot to change this as well and there is no error to inform me.
# Instead a bug was created.
d['dogs'] = 1
Question:
Is there a way to disable the automatic creation of a key that doesn't exist through d[key] = value, and instead raise a KeyError?
Everything else should keep working though:
d = new_dict() # Works
d = new_dict(hi=1) # Works
d.update(c=5, x=2) # Works
d.setdefault('9', 'something') # Works
d['a_new_key'] = 1 # Raises KeyError
You could create a child of dict with a special __setitem__ method that refuses keys that didn't exist when it was initially created:
class StrictDict(dict):
def __setitem__(self, key, value):
if key not in self:
raise KeyError("{} is not a legal key of this StricDict".format(repr(key)))
dict.__setitem__(self, key, value)
x = StrictDict({'puppies': 4, 'big_dogs': 2})
x["puppies"] = 23 #this works
x["dogs"] = 42 #this raises an exception
It's not totally bulletproof (it will allow x.update({"cats": 99}) without complaint, for example), but it prevents the most likely case.
Inherit dict class and override __setitem__ to suits your needs.Try this
class mydict(dict):
def __init__(self, *args, **kwargs):
self.update(*args, **kwargs)
def __setitem__(self, key, value):
raise KeyError(key)
>>>a=mydict({'a':3})
>>>d
{'a': 3}
>>>d['a']
3
>>>d['b']=4
KeyError: 'b'
This will only allow new keys to be added with key=value using update:
class MyDict(dict):
def __init__(self, d):
dict.__init__(self)
self.instant = False
self.update(d)
def update(self, other=None, **kwargs):
if other is not None:
if isinstance(other, dict):
for k, v in other.items():
self[k] = v
else:
for k, v in other:
self[k] = v
else:
dict.update(self, kwargs)
self.instant = True
def __setitem__(self, key, value):
if self.instant and key not in self:
raise KeyError(key)
dict.__setitem__(self, key, value)
x = MyDict({1:2,2:3})
x[1] = 100 # works
x.update(cat=1) # works
x.update({2:200}) # works
x["bar"] = 3 # error
x.update({"foo":2}) # error
x.update([(5,2),(3,4)]) # error

Set a value deep in a dict dynamically

If I have a nested dict d = {'a':{'b':{}}} and a string 'a.b.c' and a value 'X'
I need to put the value in the dict based on the key string.
What I want to achieve can be hard coded as d['a']['b']['c'] = 'X' but I need to do it dynamically. The keystring could be of any length.
For bonus points: I also need to create keys if they don't exist like 'a.b.z' but I'm sure I can figure that out if I can work out the case where they already exist.
def set(d, key, value):
dd = d
keys = key.split('.')
latest = keys.pop()
for k in keys:
dd = dd.setdefault(k, {})
dd.setdefault(latest, value)
d = {}
set(d, 'a.b.c', 'X')
set(d, 'a.b.d', 'Y')
print(d)
Result:
{'a': {'b': {'c': 'X', 'd': 'Y'}}}
def recursedict(d,keylist,value=None):
key = keylist.pop(0) # removes and returns the first key
if len(keylist): # True if there are more levels to go down
try: assert type(d[key]) is dict
except KeyError: d[key] = dict()
except AssertionError: raise ValueError("d[{}] is a {}".format(key,type(d[key])))
# if d[key] doesn't exist, make it a dict()
# if d[key] DOES exist, and isn't a dict, raise a KeyError
recursedict(d[key],keylist,value)
# recurse
else:
if value is None:
return d[key]
else:
d[keylist[0]] = value
return value
def setdeepdict(d,attributestr,value): # double entendre intentional
keys = attributestr.split('.')
recursedict(d,keys,value)
def getdeepdict(d,attributestr): # double entendre SUPER intentional
keys = attributestr.split('.')
recursedict(d,keys)

Python - Append to list nested in dict subclass

I have a dict subclass whose job is to dynamically add nested dict key if it not exists and do list append if append is called:
class PowerDict(dict):
def __getitem__(self, item):
try:
return dict.__getitem__(self, item)
except KeyError:
value = self[item] = type(self)()
return value
def append(self,item):
if type(self) != list:
self = list()
self.append(item)
so
a = PowerDict()
a['1']['2'] = 3
produce output:
a = {'1': {'2': 3}}
However, sometime i need to do something like this:
b = PowerDict()
b['1']['2'].append(3)
b['1']['2'].append(4)
should produce output:
b = {'1': {'2': [3, 4]}}
but above code produce output:
{'1': {'2': {}}}
What i am missing?
class PowerDict(dict):
# http://stackoverflow.com/a/3405143/190597 (gnibbler)
def __init__(self, parent = None, key = None):
self.parent = parent
self.key = key
def __missing__(self, key):
self[key] = PowerDict(self, key)
return self[key]
def append(self, item):
self.parent[self.key] = [item]
def __setitem__(self, key, val):
dict.__setitem__(self, key, val)
try:
val.parent = self
val.key = key
except AttributeError:
pass
a = PowerDict()
a['1']['2'] = 3
print(a)
b = PowerDict()
b['1']['2'].append(3)
b['1']['2'].append(4)
print(b)
a['1']['2'] = b
a['1']['2'].append(5)
print(a['1']['2'])
yields
{'1': {'2': 3}}
{'1': {'2': [3, 4]}}
[5]
Your append() method never works. By doing self = list() you're just reassigning the name self to a new list, which is then thrown away.
And I don't understand what you're trying to do - from getitem, you're creating new dictionaries on-the-fly if something is missing... how would you mix list behaviour in?
One of your problems is reassigning self, however, that's not it. Try printing out the value of self in the append command, and you can see another problems: The loop enters an infinite recursion. This is because you're calling the append command on a powerDict in your append command!
This should solve your problem without re-writing the append command, but I strongly suggest you re-write it anyway to avoid the above-mentioned problem:
b['1']['2']= [3]
b['1']['2'].append(4)

How can I report an error if the existing dictionary sees another item in python with the same key but different value

Firstly, Suppose I have a dictionary like given below:
temp = {'A': 3, 'S': 1}
Now if I encounter an item like 'A': 4 will it be added to the dictionary something like:
temp = {'A': 4, 'S': 1}
leaving behind the previously value of key A which was 3
Secondly, if my dictionary is
{'A': 3, 'S': 1}
How can I report an error if the dictionary sees another item like 'A': 4 or 'S': 5
You can test to see if a key already exists in a dictionary:
if 'A' in temp:
# report the error
For merging two dictionaries you can test to see if the keys overlap by creating sets out of them and ensuring the intersection is empty:
if set(temp.keys()).intersection(set(other.keys())):
# report the error
If it's OK to have a duplicate key as long as it's the same value, a simple change to the above will give it to you:
if 'A' in temp and temp['A'] != 4:
# can't insert the new value 'A': 4
if [True for x in set(temp.keys()).intersection(set(other.keys())) if temp[x] != other[x]]:
# at least one value in temp doesn't match a value in other
Looking for something like this?
temp = {
'A': 3
'S' : 1
}
def insert_or_raise(k, v) {
global temp # assuming temp is global and accessible
val = temp.get(k, None)
if not val:
temp[k] = v
return
if v != val:
raise Error("values are not same , already inserted %s for key %s " % (val, k))
}
insert('B', 1) # inserts okay
insert('B', 1) # does nothing, same key, value pair exists
insert('B', 2) # raise Error value is not 1 for key B
def strictInsert( existingDict, key, value ):
# check to see if the key is present
if key in existingDict:
# assuming you only want an error if the new value is
# different from the old one...
if existingDict[key] != value:
# raise an error
raise ValueError( "Key '%s' already in dict"%key )
else:
# insert into the dict
existingDict[key] = value
temp = {'A': 3, 'S': 1}
strictInsert( temp, 'A', 4 )
This yields:
Traceback (most recent call last):
File "so.py", line 15, in <module>
strictInsert( temp, 'A', 4 )
File "so.py", line 8, in strictInsert
raise ValueError( "Key '%s' already in dict"%key )
ValueError: Key 'A' already in dict
The best way to do this is probably to subclass dict and override __setitem__() to raise an exception when the key already exists. Unless someone knows of a pre-existing write-once dictionary in collections or something...
class WriteOnceDict(dict):
def __setitem__(self, key, value):
try:
retrieved_value = self[key]
except KeyError:
super(WriteOnceDict, self).__setitem__(key, value)
if retrieved_value != value:
raise KeyError('Different value already added for %s.' % key)
mydict = WriteOnceDict()
for key, value in input_data: #or appropriate code for whatever your input data is
mydict[key] = value

Categories

Resources