from typing import Dict


def in_obiectum_planum(
        rem: Dict,
        pydictsep: str = '.',
        pylistsep: str = ' ') -> dict:
    """in_obiectum_planum Flatten a nested python object

    Trivia:
      - obiectum, https://en.wiktionary.org/wiki/obiectum#Latin
      - recursiōnem, https://en.wiktionary.org/wiki/recursio#Latin
      - praefīxum, https://en.wiktionary.org/wiki/praefixus#Latin
      - plānum, https://en.wiktionary.org/wiki/planus

    Args:
        rem (dict): The object to flatten
        pydictsep (pydictsep, optional): The separator for python dict keys
        pylistsep (pydictsep, optional): The separator for python list values

    Returns:
        [dict]: A flattened python dictionary

    Exemplōrum gratiā (et Python doctest, id est, testum automata):

    >>> testum1 = {'a0': {'a1': {'a2': 'va'}}, 'b0': [1, 2, 3]}
    >>> in_obiectum_planum(testum1)
    {'a0.a1.a2': 'va', 'b0': '1 2 3'}

    >>> in_obiectum_planum(testum1)
    {'a0.a1.a2': 'va', 'b0': '1 2 3'}

    >>> in_obiectum_planum(testum1, pylistsep=',')
    {'a0.a1.a2': 'va', 'b0': '1,2,3'}

    >>> in_obiectum_planum(testum1, pydictsep='->')
    {'a0->a1->a2': 'va', 'b0': '1 2 3'}

    # This is not designed to flat arrays, str, None, int, ..., only dict
    >>> in_obiectum_planum([1, 2, 3, 4])
    Traceback (most recent call last):
    ...
    TypeError: in_obiectum_planum non dict<class 'list'>
    """
    resultatum = {}

    if not isinstance(rem, dict):
        raise TypeError('in_obiectum_planum non dict' + str(type(rem)))

    def recursionem(rrem, praefixum: str = ''):
        praefixum_ad_hoc = '' if praefixum == '' else praefixum + pydictsep
        if isinstance(rrem, dict):
            for clavem in rrem:
                recursionem(rrem[clavem], praefixum_ad_hoc + clavem)
        elif isinstance(rrem, list):
            resultatum[praefixum] = pylistsep.join(map(str, rrem))
        else:
            resultatum[praefixum] = rrem

    recursionem(rem)

    return resultatum