Source code for negmas.generics

r"""A set of generic classes and corresponding functions to iterate and index them.


The main goal of having this module is to allow for flexible modeling of collections with *most* python builtin
collections (e.g. `list`, `tuple`, `generator`) or even `Callable`\ s without awkward `isinstance` calls in
other modules of the library.

"""
from __future__ import annotations
from typing import Any, Callable, Iterable, Mapping, Sequence, Union

__all__ = [
    "IterableMapping",  # A mapping combining dicts, lists, tuples, and generators
    "GenericMapping",
    # An iterable mapping or a callable (e.g. function/functor)
    # GenericMapping functions (apply to GMapping and IterableMapping)
    "gmap",  # An index on an IterableMapping or a call on a Callable
    "gget",
    # Like `gmap` but can return a default value
    # IterableMapping functions (apply only to IterableMappings)
    "ienumerate",  # enumerates a mapping as key-value pairs
    "iitems",  # enumerates a mapping as key-value pairs
    "ivalues",  # enumerates all values in a mapping (no keys)
    "ikeys",  # enumerates all keys in a mapping (no values)
    "iget",  # Similar to `gget` but applies only to IterableMappings
]
# Generic classes that work with a variety of python mappings like collections or callables
IterableMapping = Union[Mapping, Sequence]
"""Something that can be iterated upon with Key-value pairs (e.g. list, dict, tuple)."""
GenericMapping = Union[Callable[[Any], Any], IterableMapping]
"""Something that can be indexed using [] or called using ()"""


[docs] def gmap(group: GenericMapping, param: Any) -> Any: """Calls or indexes the group by the param Args: group: Either a Callable or a Mapping param: The parameters to use for mapping Examples: >>> gmap([1, 23, 44], 1) 23 >>> gmap({"a": 3, "b": 5, "c": 4}, "c") 4 >>> gmap(lambda x: 3 * x, 20) 60 Returns: """ if hasattr(group, "__call__"): return group(param) # type: ignore return group[param] # type: ignore
[docs] def iget(x: IterableMapping, _key: Any, default=None) -> Any: """Get an item from an IterableMapping Args: x: the generic mapping _key: key (must be immutable) default: default value if no value attached with the key is found Examples: Example with a list >>> [iget([10, 20, 30], _) for _ in (0, 2, 1, -1, 4)] [10, 30, 20, 30, None] Example with a dictionary >>> [iget({"a": 10, "b": 20, "c": 30}, _) for _ in ("a", "c", "b", -1, "d")] [10, 30, 20, None, None] Example with a tuple >>> [iget((10, 20, 30), _) for _ in (0, 2, 1, -1, 4)] [10, 30, 20, 30, None] Example with a generator >>> [iget(range(10, 40, 10), _) for _ in (0, 2, 1, -1, 4)] [10, 30, 20, 30, None] Returns: """ if isinstance(x, dict): return x.get(_key, default) try: return x[_key] # type: ignore except IndexError: return default
[docs] def gget(x: GenericMapping, _key: Any, default=None) -> Any: """Get an item from an IterableMapping Args: x: the generic mapping _key: key (must be immutable) default: default value if no value attached with the key is found Examples: Example with a list >>> [gget([10, 20, 30], _) for _ in (0, 2, 1, -1, 4)] [10, 30, 20, 30, None] Example with a dictionary >>> [gget({"a": 10, "b": 20, "c": 30}, _) for _ in ("a", "c", "b", -1, "d")] [10, 30, 20, None, None] Example with a tuple >>> [gget((10, 20, 30), _) for _ in (0, 2, 1, -1, 4)] [10, 30, 20, 30, None] Example with a generator >>> [gget(range(10, 40, 10), _) for _ in (0, 2, 1, -1, 4)] [10, 30, 20, 30, None] Returns: """ if hasattr(x, "apply"): # noinspection PyBroadException try: return x(_key) # type: ignore except Exception: return default else: return iget(x, _key, default) # type: ignore
[docs] def ienumerate(x: IterableMapping) -> Iterable[tuple[Any, Any]]: """Enumerates a GenericMapping. Args: x (IterableMapping): A generic mapping (see `GenericMapping`) Examples: Example with a list >>> for k, cutoff_utility in ienumerate([10, 20, 30]): ... print(k, cutoff_utility, end="-") 0 10-1 20-2 30- Example with a dictionary >>> for k, cutoff_utility in ienumerate({"a": 10, "b": 20, "c": 30}): ... print(k, cutoff_utility, end="-") a 10-b 20-c 30- Example with a tuple >>> for k, cutoff_utility in ienumerate((10, 20, 30)): ... print(k, cutoff_utility, end="-") 0 10-1 20-2 30- Example with a generator >>> for k, cutoff_utility in ienumerate(range(10, 40, 10)): ... print(k, cutoff_utility, end="-") 0 10-1 20-2 30- Returns: a generator/iterator with tuples of key-value pairs. """ if isinstance(x, dict): return x.items() else: return enumerate(x)
iitems = ienumerate
[docs] def ivalues(x: IterableMapping) -> Iterable[Any]: """Returns all keys of the iterable. Args: x (IterableMapping): A generic mapping (see `GenericMapping`) Examples: Example with a list >>> for k in ivalues([10, 20, 30]): ... print(k, end="-") 10-20-30- Example with a dictionary >>> for k in ivalues({"a": 10, "b": 20, "c": 30}): ... print(k, end="-") 10-20-30- Example with a tuple >>> for k in ivalues((10, 20, 30)): ... print(k, end="-") 10-20-30- Example with a generator >>> for k in ivalues(range(10, 40, 10)): ... print(k, end="-") 10-20-30- Returns: a generator/iterator with tuples of key-value pairs. """ if isinstance(x, dict): return list(x.values()) else: return x
[docs] def ikeys(x: IterableMapping) -> Iterable[Any]: """Returns all keys of the iterable. Args: x (IterableMapping): A generic mapping (see `GenericMapping`) Examples: Example with a list >>> for k in ikeys([10, 20, 30]): ... print(k, end="-") 0-1-2- Example with a dictionary >>> for k in ikeys({"a": 10, "b": 20, "c": 30}): ... print(k, end="-") a-b-c- Example with a tuple >>> for k in ikeys((10, 20, 30)): ... print(k, end="-") 0-1-2- Example with a generator >>> for k in ikeys(range(10, 40, 10)): ... print(k, end="-") 0-1-2- Returns: a generator/iterator with tuples of key-value pairs. """ if isinstance(x, dict): return list(x.keys()) else: return range(len(x)) # type: ignore