from typing import Mapping
from typing import Sequence


def iterate(mapping):
    """
    Attempt to iterate over `mapping` such that key-values pairs are yielded per iteration. For
    dictionaries and other mappings, this would be the keys and values. For lists and other
    sequences, this would be the indexes and values. For other non- standard object types, some
    duck-typing will be used:

    - If `mapping` has callable ``mapping.items()`` attribute, it will be used.
    - If `mapping` has callable ``mapping.keys()`` and ``__getitem__`` attributes, then
      ``(key, mapping[key])`` will be used.
    - Otherwise, `iter(mapping)` will be returned.
    """
    if isinstance(mapping, Mapping) or callable(getattr(mapping, "items", None)):
        return mapping.items()

    if isinstance(mapping, Sequence):
        return enumerate(mapping)

    if callable(getattr(mapping, "keys", None)) and hasattr(mapping, "__getitem__"):
        return ((key, mapping[key]) for key in mapping.keys())

    return iter(mapping)