Last active
March 12, 2025 20:55
-
-
Save zachvictor/f4879c2b621a844b18437e57f99a7607 to your computer and use it in GitHub Desktop.
Python generator to iterate any sequence or mapping (list, dict, etc.) as (key/index, value) pairs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from collections.abc import Mapping, Sequence | |
from typing import Any, Iterator, Protocol, Tuple, TypeVar, runtime_checkable | |
K = TypeVar('K') # Key type for mappings | |
V = TypeVar('V') # Value type | |
@runtime_checkable | |
class SupportsItems(Protocol[K, V]): | |
def items(self) -> Iterator[Tuple[K, V]]: ... | |
@runtime_checkable | |
class SupportsIndexing(Protocol[V]): | |
def __len__(self) -> int: ... | |
def __getitem__(self, index: int) -> V: ... | |
def iterate_collection(collection: Any) -> Iterator[Tuple[Any, Any]]: | |
""" | |
Iterate over any collection returning (key/index, value) tuples. | |
Prioritizes standard ABC inheritance checks, then falls back to Protocol checks. | |
Mimics Go's `for k, v := range ...` behavior. | |
Examples: | |
>>> # Iterate over a dictionary | |
>>> for key, value in iterate_collection({"a": 1, "b": 2}): | |
... print(f"{key}: {value}") | |
a: 1 | |
b: 2 | |
>>> # Iterate over a list | |
>>> for index, value in iterate_collection([10, 20, 30]): | |
... print(f"Element {index} is {value}") | |
Element 0 is 10 | |
Element 1 is 20 | |
Element 2 is 30 | |
>>> # Process characters in a string with their positions | |
>>> for pos, char in iterate_collection("abc"): | |
... print(f"Character at position {pos}: {char}") | |
Character at position 0: a | |
Character at position 1: b | |
Character at position 2: c | |
Args: | |
collection: A Mapping, Sequence, string, or any object that supports | |
either .items() or indexing with __len__ | |
Yields: | |
tuple: (key/index, value) pairs | |
Raises: | |
TypeError: If the collection doesn't match any supported type or protocol | |
""" | |
# First try standard ABC checks (checking inheritance is faster than protocol checks) | |
if isinstance(collection, Mapping): | |
yield from collection.items() | |
elif isinstance(collection, Sequence): | |
yield from enumerate(collection) | |
# Fall back to protocol checks for duck-typing compatibility | |
elif isinstance(collection, SupportsItems): | |
yield from collection.items() | |
elif isinstance(collection, SupportsIndexing): | |
for i in range(len(collection)): | |
yield i, collection[i] | |
else: | |
raise TypeError( | |
f"Unsupported collection type: {type(collection).__name__} " | |
"(must be a Mapping, Sequence, or support .items() or indexing)") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment