def flatten(d, key_as_tuple=True, sep='.', list_of_dicts=None, all_iters=None):
    """ get nested dict as flat {key:val,...},
    where key is tuple/string of all nested keys

    Parameters
    ----------
    d : object
    key_as_tuple : bool
        whether keys are list of nested keys or delimited string of nested keys
    sep : str
        if key_as_tuple=False, delimiter for keys
    list_of_dicts: str or None
        if not None, flatten lists of dicts using this prefix
    all_iters: str or None
        if not None, flatten all lists and tuples using this prefix

    Examples
    --------

    >>> from pprint import pprint

    >>> d = {1:{"a":"A"}, 2:{"b":"B"}}
    >>> pprint(flatten(d))
    {(1, 'a'): 'A', (2, 'b'): 'B'}

    >>> d = {1:{"a":"A"},2:{"b":"B"}}
    >>> pprint(flatten(d,key_as_tuple=False))
    {'1.a': 'A', '2.b': 'B'}

    >>> d = [{'a':1},{'b':[1, 2]}]
    >>> pprint(flatten(d,list_of_dicts='__list__'))
    {('__list__0', 'a'): 1, ('__list__1', 'b'): [1, 2]}

    >>> d = [{'a':1},{'b':[1, 2]}]
    >>> pprint(flatten(d,all_iters='__iter__'))
    {('__iter__0', 'a'): 1,
     ('__iter__1', 'b', '__iter__0'): 1,
     ('__iter__1', 'b', '__iter__1'): 2}

    """

    def expand(key, value):
        if is_dict_like(value):
            flatten_dict = flatten(value, key_as_tuple, sep,
                                   list_of_dicts, all_iters)
            if key_as_tuple:
                return [(key + k, v) for k, v in flatten_dict.items()]
            else:
                return [(str(key) + sep + k, v)
                        for k, v in flatten_dict.items()]
        elif is_iter_non_string(value) and all_iters is not None:
            value = {'{0}{1}'.format(all_iters, i): v
                     for i, v in enumerate(value)}
            flatten_dict = flatten(value, key_as_tuple, sep,
                                   list_of_dicts, all_iters)
            if key_as_tuple:
                return [(key + k, v) for k, v in flatten_dict.items()]
            else:
                return [(str(key) + sep + k, v)
                        for k, v in flatten_dict.items()]
        elif is_list_of_dict_like(value) and list_of_dicts is not None:
            value = {'{0}{1}'.format(list_of_dicts, i): v
                     for i, v in enumerate(value)}
            flatten_dict = flatten(value, key_as_tuple, sep,
                                   list_of_dicts, all_iters)
            if key_as_tuple:
                return [(key + k, v) for k, v in flatten_dict.items()]
            else:
                return [(str(key) + sep + k, v)
                        for k, v in flatten_dict.items()]
        else:
            return [(key, value)]

    if is_iter_non_string(d) and all_iters is not None:
        d = {'{0}{1}'.format(all_iters, i): v for i, v in enumerate(d)}
    elif is_list_of_dict_like(d) and list_of_dicts is not None:
        d = {'{0}{1}'.format(list_of_dicts, i): v for i, v in enumerate(d)}
    elif not is_dict_like(d):
        raise TypeError('d is not dict like: {}'.format(d))

    if key_as_tuple:
        items = [item for k, v in d.items() for item in expand((k,), v)]
    else:
        items = [item for k, v in d.items() for item in expand(k, v)]

    return dict(items)