3

To elaborate

sample_dict = {"key1" : {"key2" : "value_of_interest}}

Now I want to be able to make the following call

val = sample_dict.recursive_get("key2", None)

I can think of a number of cases where this would be useful. For example, getting a key-value from an api response that came as JSON.

And I know it is not difficult to write to such a function, but my question is - why is it not already included?

If it leads to bad design, please provide some examples.

saq7
  • 1,528
  • 1
  • 12
  • 25
  • 3
    What do you do when there are multiple sub dicts with `key2`, or when the top level dict itself has a `key2`? There are too many possible cases to make one all encasing function reasonable. `sample.get('key1', {}).get('key2', None)` seems pretty straightforward. – MrAlexBailey Feb 11 '16 at 16:28
  • 3
    _why is it not already included?_ Because [Explicit is better than implicit. Also, In the face of ambiguity, refuse the temptation to guess.](https://www.python.org/dev/peps/pep-0020/). – Łukasz Rogalski Feb 11 '16 at 16:28
  • 3
    What should it do here? `sample_dict = {'key1': {'key1': 'value_of_interest'}}; val = sample_dict.recursive_get('key1', None)` – Two-Bit Alchemist Feb 11 '16 at 16:29
  • 1
    `dict[key1][key2]` if you dont know key1 just loop over the dict with `for k,v in dict.items()` – user2255757 Feb 11 '16 at 16:30
  • 1
    Or how about this? `sample_dict = {'key1': ['apple', 'orange', {'key1': 'value_of_interest'}, 'pear']}` Dicts (esp. formed form JSON) can take a lot of different forms. – Two-Bit Alchemist Feb 11 '16 at 16:30
  • 1
    This method would be specific to a particular use case of dicts, not applicable to a dict in general. It would be appropriate for a subclass of dict, but not `dict` itself. – chepner Feb 11 '16 at 16:33

2 Answers2

5

Suppose, as several commenters pointed out, the key pointing to your "value of interest" appears multiple times in the nested hierarchy:

sample_dict = {'key1': {'key1': 'value_of_interest'}}
val = sample_dict.recursive_get('key1', None)

What is the value of val? The inner dict? 'value_of_interest'? What should it be? Which answer violates the principle of least astonishment?

What about a dict like this?

sample_dict = {
        'key1': [
                {'key1': 'value_of_interest'},
                {'key2': 'less_interesting_value'},
                ],
        }

Should the recursion check at every level for a possible list or array? It's not uncommon to see dicts like this, especially when formed from JSON.

The point is there's a lot of different dicts out there, which can be nested in a lot of different ways. Python is pretty well-equipped to handle them all fairly elegantly. No definition of recursive_get that I can think of, though, is.

Ethan Furman
  • 63,992
  • 20
  • 159
  • 237
Two-Bit Alchemist
  • 17,966
  • 6
  • 47
  • 82
2
def recursive(dic, key, out=[]):
    if out: return
    if key in dic: out += [dic[key]]
    for k, v in dic.items():
        if isinstance(v, dict): recursive(v, key)
    return out

think i wrote you a depth first search?

user2255757
  • 756
  • 1
  • 6
  • 24
  • `isinstanceof` -> `isinstance` – Two-Bit Alchemist Feb 11 '16 at 16:53
  • You need to pass `out` in to the recursive `recursive` call of the work it does is discarded. – Ethan Furman Feb 11 '16 at 17:13
  • 1
    http://stackoverflow.com/questions/1132941/least-astonishment-in-python-the-mutable-default-argument i dont think so, though i did not run this code – user2255757 Feb 11 '16 at 18:17
  • It works because of the [most famous Python "gotcha"](http://docs.python-guide.org/en/latest/writing/gotchas/#mutable-default-arguments). Function arguments are evaluated **once**. Every time `recursive` is called it's with a reference to the same list. – Two-Bit Alchemist Feb 14 '16 at 18:16