I am running Python 2.7.10.
I would like to have a dictionary return the value stored at a particular key in case of missing item. For example, something like that:
myD = dict(...)
return myD[key] if key in myD else myD[defaultKey]
Just to make sure it is clear, I want to call myD[key] and have the right value returned without the extra if...else in my code...
This isn't quite what defaultdict does (since it takes a function to call as a default) and not quite what dict.setdefault() does, and myD.get(key, ???) does not seem to help either. I probably should inherit from dict or defaultdict and overload __init__() and missing() methods, but I could not come up with a good way to do this.
3 Answers 3
In your case, dict.get should work(I know you already mentioned it didn't work). Did you try:
myD.get(key,myD[defaultKey])
5 Comments
defaultdict but overrides the defaultfactory method?.get() to the above codedefaultfactory actually changes the dict to have a new thing. get returns a reference to an existing thing. So depending on what you want...I'm not completely sure what you want (didn't read all the comments under your question), but think this may be at least close to what you want.
class DefaultKeyDict(dict):
def __init__(self, default_key, *args, **kwargs):
self.default_key = default_key
super(DefaultKeyDict, self).__init__(*args, **kwargs)
def __missing__ (self, key):
if self.default_key not in self: # default key not defined
raise KeyError(key)
return self[self.default_key]
def __repr__(self):
return ('{}({!r}, {})'.format(self.__class__.__name__,
self.default_key,
super(DefaultKeyDict, self).__repr__()))
def __reduce__(self): # optional, for pickle support
args = (self.default_key if self.default_key in self else None,)
return self.__class__, args, None, None, self.iteritems()
dkdict = DefaultKeyDict('b', {'a': 1, 'b': 2, 'c': 3})
print dkdict['a'] # -> 1
print dkdict['b'] # -> 2
print dkdict['c'] # -> 3
print dkdict['d'] # -> 2 (value of the default key)
del dkdict['b'] # delete the default key
try:
print dkdict['d'] # should now raise exception like normal
except KeyError:
print("dkdict['d'] raised a KeyError")
You might want to modify the class __init__() method to accept both the default key and its value as arguments (instead of just the key).
4 Comments
__init__() as you suggest, the signature would change to def __init__(self, default_key, *args, **kwargs)? Or do I just check if default_key is in kwargs (if so, how can i distinguish this from someone actually wanting a pair like this in the dictionary itself)?None is inappropriate due to being a valid value) is to just do sentinel = object() (sentinel can be Null if you like, but I find sentinel to describe the purpose more directly) and perform all comparisons to it with is (mostly for efficiency; object's __eq__ is an identity check anyway). No need to create a relatively expensive class to define the sentinel type; object can be constructed directly to get a unique marker using almost no resources (object inst. is a mere 16 bytes on Py 3.5, vs. 1016B+56B for class + instance).def __init__(self, default_key, default_key_value, *args, **kwargs) or make it a keyword argument. To distinguish the latter from a user key/value pair, the keyword could start with an underscore, i.e. _default_key=('DefaultKey', 42). Doing something like that is, by convention, how private names are usually designated in Python.When overwriting __getitem__, one can use simply square brackets. It returns the value for the first valid key and None if no key is found.
class mDict(dict):
def __getitem__(self, keys):
for k in keys:
if k in self:
return self.get(k)
mdict = mDict({'a': 1, 'b': 2, 'default': 3})
mdict['a', 'default'] # -> 1
mdict['X', 'b', 'default'] # -> 2
mdict['X', 'Y', 'default'] # -> 3
mdict['X', 'Y', 'Z'] # -> None
One can use here also more than just two keys, which is more readable than many nested .get().
1 Comment
Explore related questions
See similar questions with these tags.
dict.getwon't do that; if you want to know how you could write your own, look at docs.python.org/2/library/… and implement__getitem__accordingly. But where shoulddefaultKeycome from (is it a parameter? An attribute of the class, or of the instance?)defaultKeyis param at initialization, that the instance should store as an attribute__[get/set/del]item__,__iter__and__len__, plus your custom__init__for the default key-value pair).defaultdict, and just overriding__getitem__, leaving same functionality everywhere else...