Why doesn't list have safe get method like dictionary?
Ultimately it probably doesn't have a safe .get
method because a dict
is an associative collection (values are associated with names) where it is inefficient to check if a key is present (and return its value) without throwing an exception, while it is super trivial to avoid exceptions accessing list elements (as the len
method is very fast). The .get
method allows you to query the value associated with a name, not directly access the 37th item in the dictionary (which would be more like what you're asking of your list).
Of course, you can easily implement this yourself:
def safe_list_get (l, idx, default):
try:
return l[idx]
except IndexError:
return default
You could even monkeypatch it onto the __builtins__.list
constructor in __main__
, but that would be a less pervasive change since most code doesn't use it. If you just wanted to use this with lists created by your own code you could simply subclass list
and add the get
method.
Python: Safe dictionary access with lists?
For dict
you can use the get
method. For lists you can just be careful with the index:
data.get('object_1', {}).get('object_2', {}).get('list', [{}])[0].get('property', default)
This is a bit awkward because it makes a new temporary dict or lost for each call get
. It's also not super safe for lists, which don't have an equivalent method.
You can wrap the getter in a small routine to support lists too, but it's not really worth it. You're better off writing a one-off utility function that uses either exception handling or preliminary checking to handle the cases you want to react to:
def get(obj, *keys, default=None):
for key in keys:
try:
obj = obj[key]
except KeyError, IndexError:
return default
return obj
Exception handing has a couple of huge advantages over doing it the other way. For one thing, you don't have to do separate checks on the key depending on whether the object is a dict or list. For another, you can support almost any other reasonable type that supports __getitem__
indexing. To show what I mean, here is the asking for permission rather than forgiveness approach:
from collections.abc import Mapping, Sequence
from operator import index
def get(obj, *keys, default=None):
for key in keys:
if isinstance(obj, Mapping):
if key not in obj:
return default
elif isinstance(obj, Sequence):
try:
idx = index(key)
except TypeError:
return default
if len(obj) <= idx or len(obj) < -idx:
return default
obj = obj[key]
return obj
Observe how awkward and error-prone the checking is. Try passing in a custom object instead of a list
, or a key that's not an integer. In Python, carefully used exceptions are your friend, and there's a reason it's pythonic to ask for forgiveness rather than for permission.
Safe method to get value of nested dictionary
You could use get
twice:
example_dict.get('key1', {}).get('key2')
This will return None
if either key1
or key2
does not exist.
Note that this could still raise an AttributeError
if example_dict['key1']
exists but is not a dict (or a dict-like object with a get
method). The try..except
code you posted would raise a TypeError
instead if example_dict['key1']
is unsubscriptable.
Another difference is that the try...except
short-circuits immediately after the first missing key. The chain of get
calls does not.
If you wish to preserve the syntax, example_dict['key1']['key2']
but do not want it to ever raise KeyErrors, then you could use the Hasher recipe:
class Hasher(dict):
# https://stackoverflow.com/a/3405143/190597
def __missing__(self, key):
value = self[key] = type(self)()
return value
example_dict = Hasher()
print(example_dict['key1'])
# {}
print(example_dict['key1']['key2'])
# {}
print(type(example_dict['key1']['key2']))
# <class '__main__.Hasher'>
Note that this returns an empty Hasher when a key is missing.
Since Hasher
is a subclass of dict
you can use a Hasher in much the same way you could use a dict
. All the same methods and syntax is available, Hashers just treat missing keys differently.
You can convert a regular dict
into a Hasher
like this:
hasher = Hasher(example_dict)
and convert a Hasher
to a regular dict
just as easily:
regular_dict = dict(hasher)
Another alternative is to hide the ugliness in a helper function:
def safeget(dct, *keys):
for key in keys:
try:
dct = dct[key]
except KeyError:
return None
return dct
So the rest of your code can stay relatively readable:
safeget(example_dict, 'key1', 'key2')
In python I'd like to eliminate some dictionary items ({n:[0,0]}) in a list of dictionaries
Your data is a list of dictionaries. Iterating over a list and changing it is a bad idea. Use a list comprehension to filter out the undesirable items instead:
out = [i for i in d if list(i.values())[0] != [0,0]]
Output:
[{1: [2.9, 1.04]}, {2: [1.45, 1.01]}, {4: [9.5, 1.55]}]
Using Dictionary get method to return empty list by default returns None instead
It's not dict.get( i, [] )
that's returning None
, it's append
. You probably want to use dict.setdefault(i, []).append(j)
or just use a defaultdict
in the first place.
Here's how you would do it:
d = {}
for i in range( 0, 10 ):
for j in range( 0, 100 ):
d.setdefault( i, [] ).append( j )
Note that I changed dict
to d
because dict
already means something in Python (you're redefining it) and I removed the superfluous dict[i] =
that was causing the error message.
Related Topics
How to Convert an Rgb Image to Numpy Array
How to Strip the Whitespace from Pandas Dataframe Headers
Matplotlib: How to Prevent X-Axis Labels from Overlapping
How to Call Python Functions Dynamically
Installing Multiple Versions of a Package with Pip
Constructing a Co-Occurrence Matrix in Python Pandas
Can One Get Hierarchical Graphs from Networkx with Python 3
How to Get Tkinter Canvas to Dynamically Resize to Window Width
Django Query That Get Most Recent Objects from Different Categories
Python Super() Raises Typeerror
How to Represent an Infinite Number in Python
How to Extract Text and Text Coordinates from a PDF File
How to Open a Website with Urllib via Proxy in Python
Python Unittest.Testcase Execution Order
How Come a File Doesn't Get Written Until I Stop the Program
How to Iterate Through Dictionary in a Dictionary in Django Template