def getdeepattr(d, keys, default_value=None):
    """
    Similar to the built-in `getattr`, this function accepts a list/tuple of keys to get a value deep in a `dict`

    Given the following dict structure

    .. code-block:: python

        d = {
          'A': {
            '0': {
              'a': 1,
              'b': 2,
            }
          },
        }

    Calling `getdeepattr` with a key path to a value deep in the structure will return that value. If the value or any
    of the objects in the key path do not exist, then the default value is returned.

    .. code-block:: python

        assert 1 == getdeepattr(d, ('A', '0', 'a'))
        assert 2 == getdeepattr(d, ('A', '0', 'b'))
        assert 0 == getdeepattr(d, ('A', '0', 'c'), default_value=0)
        assert 0 == getdeepattr(d, ('X', '0', 'a'), default_value=0)

    :param d:
        A dict value with nested dict attributes.
    :param keys:
        A list/tuple path of keys in `d` to the desired value
    :param default_value:
        A default value that will be returned if the path `keys` does not yield a value.
    :return:
        The value following the path `keys` or `default_value`
    """
    d_level = d
    for key in keys:
        if isinstance(d_level, dict) and key not in d_level or not hasattr(
            d_level, key):
            return default_value
        d_level = d_level[key]
    return d_level
