Created
November 17, 2021 15:32
-
-
Save rhettinger/cc6451b60e8ee7e9b022a6e61227ffd7 to your computer and use it in GitHub Desktop.
Emulate max() as fully as possible in pure Python.
This file contains 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
'Emulate max() as fully as possible in pure Python.' | |
# https://stackoverflow.com/questions/69997857/implementation-of-max-function-in-python/69997876#69997876 | |
from typing import TypeVar, Any, Iterator, Iterable, Optional | |
from typing import Union, Protocol, Callable, overload | |
class SupportsGT(Protocol): | |
def __gt__(self, other: Any) -> bool: | |
... | |
T = TypeVar('T', bound=SupportsGT) | |
D = TypeVar('D') | |
class Sentinel(object): | |
pass | |
sentinel = Sentinel() | |
def mymax_backend( | |
first_value: T, | |
it: Iterator[T], | |
key: Optional[Callable[[T], Any]], | |
) -> T: | |
largest = first_value | |
if key is None: | |
for x in it: | |
if x > largest: | |
largest = x | |
return largest | |
largest_key = key(largest) | |
for x in it: | |
kx = key(x) | |
if kx > largest_key: | |
largest = x | |
largest_key = kx | |
return largest | |
def mymax_argsonly(*args: T, key: Optional[Callable[[T], Any]] = None) -> T: | |
if not args: | |
raise TypeError('max expected at least 1 argument, got 0') | |
it = iter(args) | |
largest = next(it) | |
return mymax_backend(largest, it, key) | |
def mymax_iterable_only( | |
iterable: Iterable[T], | |
/, | |
*, | |
default: Union[Sentinel, D] = sentinel, | |
key: Optional[Callable[[T], Any]] = None, | |
) -> Union[D, T]: | |
it = iter(iterable) | |
largest = next(it, sentinel) | |
if isinstance(largest, Sentinel): | |
if not isinstance(default, Sentinel): | |
return default | |
raise ValueError('max() arg is an empty sequence') | |
return mymax_backend(largest, it, key) | |
@overload | |
def mymax( | |
*args: T, | |
key: Optional[Callable[[T], Any]], | |
) -> T: | |
... | |
@overload | |
def mymax( | |
iterable: Iterable[T], | |
/, | |
*, | |
default: Union[Sentinel, D], | |
key: Optional[Callable[[T], Any]], | |
) -> Union[D, T]: | |
... | |
def mymax( | |
*args: Union[T, Iterable[T]], | |
default: Union[Sentinel, D] = sentinel, | |
key: Optional[Callable[[T], Any]] = None, | |
) -> Union[D, T]: | |
"""max(iterable, *[, default=obj, key=func]) -> value | |
max(arg1, arg2, *args, *[, key=func]) -> value | |
With a single iterable argument, return its biggest item. The | |
default keyword-only argument specifies an object to return if | |
the provided iterable is empty. | |
With two or more arguments, return the largest argument. | |
""" | |
if len(args) == 1: | |
assert isinstance(args, Iterable) | |
return mymax_iterable_only(*args, default=default, key=key) | |
if default is not sentinel: | |
raise TypeError( | |
'Cannot specify a default for max() with multiple positional arguments' | |
) | |
return mymax_argsonly(*args, key=key) | |
assert mymax_argsonly('bob', 'adam') == 'bob' | |
assert mymax_argsonly('adam', 'bob') == 'bob' | |
assert mymax_argsonly('bob', 'adam', key=len) == 'adam' | |
assert mymax_argsonly('adam', 'bob', key=len) == 'adam' | |
assert mymax('bob', 'adam') == 'bob' | |
assert mymax('adam', 'bob') == 'bob' | |
assert mymax('bob', 'adam', key=len) == 'adam' | |
assert mymax('adam', 'bob', key=len) == 'adam' | |
assert mymax_iterable_only(['bob', 'adam']) == 'bob' | |
assert mymax_iterable_only(['adam', 'bob']) == 'bob' | |
assert mymax_iterable_only([], default=10) == 10 | |
assert mymax_iterable_only(['bob', 'adam'], key=len) == 'adam' | |
assert mymax_iterable_only(['adam', 'bob'], key=len) == 'adam' | |
assert mymax(['bob', 'adam']) == 'bob' | |
assert mymax(['adam', 'bob']) == 'bob' | |
assert mymax([], default=10) == 10 | |
assert mymax(['bob', 'adam'], key=len) == 'adam' | |
assert mymax(['adam', 'bob'], key=len) == 'adam' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment