I rely pretty heavily on Python’s min
and max
function when trying to take the highest or lowest values from a particular algorithm. For example, a regular expression extracts scores (these could be grades, number of pages in a book, distance run, etc.) from an input document. The document may contain multiple scores (e.g., describing all of someone’s grades on an exam, or all the distances someone ran in a week), and I only want the best or the worst value for subsequent analysis. Easy:
def get_scores_from_document(document): for m in rx_pattern.finditer(document): yield m.group() scores = get_scores_from_document(document) best_score = max(scores) return best_score # or, the elegant one-liner return max(get_scores_from_document(document))
I have essentially written this block of code many times which naively assumes that there will always be score. What happens, however, when the document contains no scores? After broadening my input from the small sample to the full input, it inevitably fails.
ValueError: max() arg is an empty sequence
My solution to date as been adding a length check (or a try-except in the calling function):
scores = list(get_scores_from_document(document)) if scores: best_score = max(scores) else: best_score = None return best_score
Farewell pithy one-liner, but it works. I only recently discovered, however, that I needn’t have bid it adieu.
return max(get_scores_from_document(document), default=None)
This works for min
as well. Essentially, it says that if there’s no “max” or “min” value, use a default. The documentation can be seen here: https://docs.python.org/3/library/functions.html#max.
Another useful ‘default’ parameter can be found in the dictionary methods, e.g., dict.get, which also returns a default value rather than raising a KeyError
.
None == {'a': 1}.get('b', None) # True # or equivalent to try: return {'a': 1}['b'] except KeyError: return None