The OP requested the following behavior
>>> d.recursive_get('bogus key', default='nonexistent key')
'nonexistent key'
(As of June 15, 22022) none of the up-voted answers accomplish this, so I have modified @ThomasOrozco's solution to resolve this
from functools import reduce
def rget(d, *keys, default=None):
    """Use a sentinel to handle both missing keys AND alternate default values"""
    sentinel = {}
    v = reduce(lambda c, k: c.get(k, sentinel), keys, d)
    if v is sentinel:
        return default
    return v
Below is a complete, unit-test-like demonstration of where the other answers have issues. I've named each approach according to its author. Note that this answer is the only one which passes all 4 test cases, namely
- Basic retrieval when key-tree exists
- Non-existent key-tree returns None
- Option to specify a default aside from None
- Values which are an empty dict should return as themselves rather than the default
from functools import reduce
def thomas_orozco(d, *keys):
    return reduce(lambda c, k: c.get(k, {}), keys, d)
def jpp(dataDict, *mapList):
    """Same logic as thomas_orozco but exits at the first missing key instead of last"""
    try:
        return reduce(dict.get, *mapList, dataDict)
    except TypeError:
        return None
def sapi(d, *args, default=None):
    if not args:
        return d
    key, *args = args
    return sapi(d.get(key, default), *args, default=default)
def rget(d, *keys, default=None):
    sentinel = {}
    v = reduce(lambda c, k: c.get(k, sentinel), keys, d)
    if v is sentinel:
        return default
    return v
def assert_rget_behavior(func):
    """Unit tests for desired behavior of recursive dict.get()"""
    fail_count = 0
    # Basic retrieval when key-tree exists
    d = {'foo': {'bar': 'baz', 'empty': {}}}
    try:
        v = func(d, 'foo', 'bar')
        assert v == 'baz', f'Unexpected value {v} retrieved'
    except Exception as e:
        print(f'Case 1: Failed basic retrieval with {repr(e)}')
        fail_count += 1
    # Non-existent key-tree returns None
    try:
        v = func(d, 'bogus', 'key')
        assert v is None, f'Missing key retrieved as {v} instead of None'
    except Exception as e:
        print(f'Case 2: Failed missing retrieval with {repr(e)}')
        fail_count += 1
    # Option to specify a default aside from None
    default = 'alternate'
    try:
        v = func(d, 'bogus', 'key', default=default)
        assert v == default, f'Missing key retrieved as {v} instead of {default}'
    except Exception as e:
        print(f'Case 3: Failed default retrieval with {repr(e)}')
        fail_count += 1
    # Values which are an empty dict should return as themselves rather than the default
    try:
        v = func(d, 'foo', 'empty')
        assert v == {}, f'Empty dict value retrieved as {v} instead of {{}}'
    except Exception as e:
        print(f'Case 4: Failed retrieval of empty dict value with {repr(e)}')
        fail_count += 1
    # Success only if all pass
    if fail_count == 0:
        print('Passed all tests!')
if __name__ == '__main__':
    assert_rget_behavior(thomas_orozco)  # Fails cases 2 and 3
    assert_rget_behavior(jpp)  # Fails cases 1, 3, and 4
    assert_rget_behavior(sapi)  # Fails cases 2 and 3
    assert_rget_behavior(rget)  # Only one to pass all 3