Skip to content

Instantly share code, notes, and snippets.

@wonderbeyond
Last active July 9, 2023 06:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wonderbeyond/fdd874f37d86534f23109f153cb671a2 to your computer and use it in GitHub Desktop.
Save wonderbeyond/fdd874f37d86534f23109f153cb671a2 to your computer and use it in GitHub Desktop.
Safe alternative of min/max in Python (the builtin min/max is evil)
"""
Python's builtin min function is evil
>>> min(2, 1)
1
>>> min([2, 1])
1
>>> min([2])
2
>>> min([])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: min() arg is an empty sequence
>>> min([None])
>>> min([None, 1])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: int() < NoneType()
>>> min(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
>>> min(1, None)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: NoneType() < int()
>>>
"""
from collections import Iterable
def _min_max_ignore_none(agg, it=None, *args, **kwargs):
"""
>>> min = min_ignore_none
>>> max = max_ignore_none
>>> min(2, 1), min([2, 1])
(1, 1)
>>> min(1, None), min(None, 1), min([1, None])
(1, 1, 1)
>>> min(1), min([1]), min(*[1])
(1, 1, 1)
>>> min(), min([]), min(*[])
(None, None, None)
>>> min(x for x in [])
>>> min(None), min([None])
(None, None)
>>> max(1, 2), max([1, 2])
(2, 2)
"""
assert agg in (min, max)
if isinstance(it, Iterable) and not args:
it = [v for v in it if v is not None]
if not len(it):
return None
if args:
it = list(args) + [it]
it = [v for v in it if v is not None]
if not len(it):
return None
elif it:
it = it if isinstance(it, Iterable) else [it]
else:
return None
return agg(it, **kwargs)
def min_ignore_none(*a, **kw):
return _min_max_ignore_none(min, *a, **kw)
def max_ignore_none(*a, **kw):
return _min_max_ignore_none(max, *a, **kw)
@vbrozik
Copy link

vbrozik commented Jun 30, 2022

Your function is almost like a decorator but not really. I would recommend you looking for example at https://realpython.com/primer-on-python-decorators/

Then you can create a decorator which will create your functions like this:

min_ignore_none = _min_max_ignore_none(min)
max_ignore_none = _min_max_ignore_none(max)

# and possibly for other aggregate functions:
sum_ignore_none = _min_max_ignore_none(sum)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment