From e8d5ea34270f4ef4316a70df7b8117e8b08b889c Mon Sep 17 00:00:00 2001 From: Scott Gasch Date: Thu, 8 Jun 2023 13:59:13 -0700 Subject: [PATCH] Comparable.. and better type hints on dict_utils. --- src/pyutils/collectionz/bst.py | 25 ++--------------------- src/pyutils/dict_utils.py | 37 ++++++++++++++++++---------------- src/pyutils/typez/typing.py | 30 +++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 40 deletions(-) create mode 100644 src/pyutils/typez/typing.py diff --git a/src/pyutils/collectionz/bst.py b/src/pyutils/collectionz/bst.py index 7bc5eeb..74c328d 100644 --- a/src/pyutils/collectionz/bst.py +++ b/src/pyutils/collectionz/bst.py @@ -4,30 +4,9 @@ """A binary search tree implementation.""" -from abc import abstractmethod -from typing import Any, Generator, List, Optional, Protocol +from typing import Generator, List, Optional - -class Comparable(Protocol): - """Anything that implements basic comparison methods such that it - can be compared to other instances of the same type. - - Check out :meth:`functools.total_ordering` - (https://docs.python.org/3/library/functools.html#functools.total_ordering) - for an easy way to make your type comparable. - """ - - @abstractmethod - def __lt__(self, other: Any) -> bool: - ... - - @abstractmethod - def __le__(self, other: Any) -> bool: - ... - - @abstractmethod - def __eq__(self, other: Any) -> bool: - ... +from pyutils.typez.typing import Comparable class Node: diff --git a/src/pyutils/dict_utils.py b/src/pyutils/dict_utils.py index 1071bf5..3c0be25 100644 --- a/src/pyutils/dict_utils.py +++ b/src/pyutils/dict_utils.py @@ -5,14 +5,17 @@ """This module contains helper functions for dealing with Python dictionaries.""" from itertools import islice -from typing import Any, Callable, Dict, Iterator, List, Tuple +from typing import Any, Callable, Dict, Hashable, Iterator, List, Tuple from pyutils import dataclass_utils +from pyutils.typez.typing import Comparable + +AnyDict = Dict[Hashable, Any] def init_or_inc( - d: Dict[Any, Any], - key: Any, + d: AnyDict, + key: Hashable, *, init_value: Any = 1, inc_function: Callable[..., Any] = lambda x: x + 1, @@ -50,7 +53,7 @@ def init_or_inc( return False -def shard(d: Dict[Any, Any], size: int) -> Iterator[Dict[Any, Any]]: +def shard(d: AnyDict, size: int) -> Iterator[AnyDict]: """ Shards (i.e. splits) a dict into N subdicts which, together, contain all keys/values from the original unsharded dict. @@ -117,10 +120,10 @@ def raise_on_duplicated_keys(key, new_value, old_value): def coalesce( - inputs: Iterator[Dict[Any, Any]], + inputs: Iterator[AnyDict], *, aggregation_function: Callable[[Any, Any, Any], Any] = coalesce_by_creating_list, -) -> Dict[Any, Any]: +) -> AnyDict: """Coalesce (i.e. combine) N input dicts into one output dict ontaining the union of all keys / values in every input dict. When keys collide, apply the aggregation_function which, by @@ -163,7 +166,7 @@ def coalesce( ... Exception: Key b is duplicated in more than one input dict. """ - out: Dict[Any, Any] = {} + out: AnyDict = {} for d in inputs: for key in d: if key in out: @@ -174,7 +177,7 @@ def coalesce( return out -def item_with_max_value(d: Dict[Any, Any]) -> Tuple[Any, Any]: +def item_with_max_value(d: AnyDict) -> Tuple[Hashable, Any]: """ Args: d: a dict with comparable values @@ -195,7 +198,7 @@ def item_with_max_value(d: Dict[Any, Any]) -> Tuple[Any, Any]: return max(d.items(), key=lambda _: _[1]) -def item_with_min_value(d: Dict[Any, Any]) -> Tuple[Any, Any]: +def item_with_min_value(d: AnyDict) -> Tuple[Hashable, Any]: """ Args: d: a dict with comparable values @@ -212,7 +215,7 @@ def item_with_min_value(d: Dict[Any, Any]) -> Tuple[Any, Any]: return min(d.items(), key=lambda _: _[1]) -def key_with_max_value(d: Dict[Any, Any]) -> Any: +def key_with_max_value(d: AnyDict) -> Hashable: """ Args: d: a dict with comparable keys @@ -232,7 +235,7 @@ def key_with_max_value(d: Dict[Any, Any]) -> Any: return item_with_max_value(d)[0] -def key_with_min_value(d: Dict[Any, Any]) -> Any: +def key_with_min_value(d: AnyDict) -> Hashable: """ Args: d: a dict with comparable keys @@ -252,7 +255,7 @@ def key_with_min_value(d: Dict[Any, Any]) -> Any: return item_with_min_value(d)[0] -def max_value(d: Dict[Any, Any]) -> Any: +def max_value(d: AnyDict) -> Any: """ Args: d: a dict with compatable values @@ -267,7 +270,7 @@ def max_value(d: Dict[Any, Any]) -> Any: return item_with_max_value(d)[1] -def min_value(d: Dict[Any, Any]) -> Any: +def min_value(d: AnyDict) -> Any: """ Args: d: a dict with comparable values @@ -282,7 +285,7 @@ def min_value(d: Dict[Any, Any]) -> Any: return item_with_min_value(d)[1] -def max_key(d: Dict[Any, Any]) -> Any: +def max_key(d: Dict[Comparable, Any]) -> Comparable: """ Args: d: a dict with comparable keys @@ -300,7 +303,7 @@ def max_key(d: Dict[Any, Any]) -> Any: return max(d.keys()) -def min_key(d: Dict[Any, Any]) -> Any: +def min_key(d: Dict[Comparable, Any]) -> Comparable: """ Args: d: a dict with comparable keys @@ -318,7 +321,7 @@ def min_key(d: Dict[Any, Any]) -> Any: return min(d.keys()) -def parallel_lists_to_dict(keys: List[Any], values: List[Any]) -> Dict[Any, Any]: +def parallel_lists_to_dict(keys: List[Hashable], values: List[Any]) -> AnyDict: """Given two parallel lists (keys and values), create and return a dict. @@ -339,7 +342,7 @@ def parallel_lists_to_dict(keys: List[Any], values: List[Any]) -> Dict[Any, Any] return dict(zip(keys, values)) -def dict_to_key_value_lists(d: Dict[Any, Any]) -> Tuple[List[Any], List[Any]]: +def dict_to_key_value_lists(d: AnyDict) -> Tuple[List[Hashable], List[Any]]: """Given a dict, decompose it into a list of keys and values. Args: diff --git a/src/pyutils/typez/typing.py b/src/pyutils/typez/typing.py new file mode 100644 index 0000000..18cf4ef --- /dev/null +++ b/src/pyutils/typez/typing.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 + +# © Copyright 2021-2023, Scott Gasch + +"""My type hints.""" + +from abc import abstractmethod +from typing import Any, Protocol + + +class Comparable(Protocol): + """Anything that implements basic comparison methods such that it + can be compared to other instances of the same type. + + Check out :meth:`functools.total_ordering` + (https://docs.python.org/3/library/functools.html#functools.total_ordering) + for an easy way to make your type comparable. + """ + + @abstractmethod + def __lt__(self, other: Any) -> bool: + ... + + @abstractmethod + def __le__(self, other: Any) -> bool: + ... + + @abstractmethod + def __eq__(self, other: Any) -> bool: + ... -- 2.46.0