[Python] Structure-preserving nested map in Python

Discussão em 'Python' iniciado por Stack, Maio 19, 2025.

  1. Stack

    Stack Membro Participativo

    I'm writing a Python function inspired by map that takes a function f: Callable[[A], B] and an arbitrarily nested structure of iterables (e.g. lists of tuples of lists of ... of objects of type A) and returns an identically nested structure with f being applied to all "leaf nodes" of the original structure. Ideally, it would support dict[Any, A] too and "homogenous" NamedTuples by applying f only to the values and leaving the keys untouched (NamedTuples' fields should all have type A for this to work).

    For example:

    >>> f = lambda x: x * 2
    >>> input = [([1, 2], [3, 4]), {"foo": 5, "bar": 6}]
    >>> nested_map(f, input)
    [([2, 4], [6, 8]), {"foo": 10, "bar": 12}]


    Ideally, it would be type-hint friendly in the sense that it would tell a static checker that the output object has exactly the same structure as the input. Although this is probably way harder to achieve.

    I have a kind of working version for this but:

    • the code feels really ugly;
    • I was unable to properly type-annotate "homogenous" NamedTuples; and
    • it doesn't work well with static type checking.

    import inspect
    from collections.abc import Iterable
    from typing import Any, Callable, Generator, NamedTuple, cast

    type MappableIterable[A] = list[A] | tuple[A] | dict[Any, A] | Generator[A]

    type NestedIterable[A] = A | MappableIterable["NestedIterable[A]"]

    def nested_map[A, B](
    f: Callable[[A], B], nested_structure: NestedIterable[A]
    ) -> NestedIterable:
    """
    Takes a nested iterable structure and applies f to all of its nodes,
    returning them in the same structure.
    """

    structure = type(nested_structure)
    match nested_structure:
    case dict():
    return {
    k: nested_map(f, v)
    for k, v in nested_structure.items()
    }
    # NamedTuple
    case _ if hasattr(structure, "_fields") and isinstance(
    getattr(structure, "_fields"), tuple
    ):
    d = {
    k: nested_map(f, v) for
    k, v in cast(NamedTuple, nested_structure)._asdict().items()
    }
    return cast(NestedIterable, structure(**d))
    case _ if inspect.isgenerator(nested_structure):
    return ( nested_map(f, s) for s in nested_structure )
    # Have to check str first since it is an Iterable
    case str():
    return f(nested_structure)
    case Iterable():
    structure = cast(type
    • , structure)
      return structure(map(lambda s: nested_map(f, s), nested_structure))
      case _:
      return f(nested_structure)


      Do you think there is a "more elegant" way to achieve this?

      Continue reading...

Compartilhe esta Página