11

I am trying to find a better way to implement this:

d = {"a": {"b": {"c": 4}}} 
l = ["a", "b", "c"]
for x in l:
    d = d[x]
print (d) # 4 

I am learning functional programming so I am just trying random example that come to my head :)

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Ankit Solanki
  • 670
  • 2
  • 9
  • 23
  • Your are indirectly calling d = d["c"] which is 4... – Nilesh Dec 02 '13 at 09:34
  • Hi Nilesh, Thanks for replying, but i guess I failed to explain my question correctly to you. Please Martijn Answer below. Anyways thanks for the help :) Cheers! – Ankit Solanki Dec 02 '13 at 09:45
  • related: [Python: Change values in dict of nested dicts using items in a list](http://stackoverflow.com/q/11918852/4279) – jfs Feb 23 '15 at 11:33

1 Answers1

23

Use reduce():

reduce(dict.__getitem__, l, d)

or better still, using operator.getitem():

from operator import getitem

reduce(getitem, l, d)

Demo:

>>> d = {"a": {"b": {"c": 4}}} 
>>> l = ["a", "b", "c"]
>>> from operator import getitem
>>> reduce(getitem, l, d)
4

Python 3 moved the reduce() function out of the built-ins and into functools.reduce().

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Why not simply `dict.get`? – georg Dec 02 '13 at 10:53
  • 1
    @thg435: Both work, both lead to an error if a key is missing, but the `dict.__getitem__` error would be clearer, I'd say (`KeyError` vs. `AttributeError`). – Martijn Pieters Dec 02 '13 at 11:00
  • From a style point of view, is it better to use `dict.__getitem__` or `lambda d, k: d[k]` here? – Eric Dec 19 '13 at 14:29
  • 1
    @Eric: There is a 3rd option: `dict.get`. I prefer `dict.__getitem__` here; the error message is clearer when there is a key missing (`dict.get` will get you a `TypeError: 'NoneType' object has no attribute '__getitem__'`). The `lambda` is a lot more verbose and slower. – Martijn Pieters Dec 19 '13 at 14:30
  • [`operator.getitem`](http://docs.python.org/2/library/operator.html#operator.getitem) was the function I was looking for, which has the advantage of working on nested lists as well – Eric Dec 19 '13 at 14:34
  • @MartijnPieters: You're confusing it with `itemgetter`, which is exactly the mistake I would have made 5 minutes ago :) – Eric Dec 19 '13 at 14:38
  • 1
    @Eric: bah, so I did. `operator.getitem()` is a great idea, works beautifully, and will get a place in this answer! – Martijn Pieters Dec 19 '13 at 14:40
  • @Eric: [as far as I remember](http://stackoverflow.com/a/11919150/4279), `operator.getitem()` may produce different exceptions from `dict.__getitem__` -- whether it is important depends on the application. – jfs Feb 23 '15 at 11:35
  • @J.F.Sebastian: not sure that it does; it raises `KeyError` just fine for me. Do you have any specifics? `operator.getitem` is just the C `PyObject_GetItem` function, which is what all C code uses to access keys on dictionaries. – Martijn Pieters Feb 23 '15 at 11:39
  • Consider what happens if you give a list to operator.getitem and dict.__getitem__ – jfs Feb 23 '15 at 11:41
  • @J.F.Sebastian: `{}.__getitem__('foo')` and `operator.getitem({}, 'foo')` both raise a `KeyError`. Are you talking about the *attribute error* when the object doesn't have a `__getitem__` method? In that case the exceptions differ, yes; `AttributeError` vs. `TypeError`. But that hardly matters here, does it? – Martijn Pieters Feb 23 '15 at 11:46
  • @J.F.Sebastian: that's because `operator.getitem(obj, key)` follows the ``obj[key]` notation (the same C function is used when the bytecode for indexing is executed). – Martijn Pieters Feb 23 '15 at 11:49
  • You misunderstood: consider the difference between `reduce(dict.__getitem__, l, d)` and `reduce(operator.getitem, l, d)` -- if `d` has a list instead of a dict at some level then `dict.__getitem__` raises `TypeError` while `operator.getitem` might succeed accidentally or raise `IndexError` instead. – jfs Feb 23 '15 at 11:55
  • @J.F.Sebastian: right, passing in the wrong type for the first argument of `dict.__getitem__` you mean. I don't think it matters here; you'll never account for all the possible mistakes the caller of a function could make, it is their responsibility to pass in valid objects. :-) – Martijn Pieters Feb 23 '15 at 11:57
  • I prefer solutions that detect errors earlier such as `dict.__getitem__` here. – jfs Feb 23 '15 at 12:23
  • It's a very good tips. I made a function for this https://gist.github.com/av1m/3517b2eff6615c7c12a6cb31aa86fe5d – avimimoun Apr 19 '22 at 14:28