Jared Grubb wrote:
On 15 Apr 2009, at 11:17, Raymond Hettinger wrote:
[Adam Atlas]
I propose adding a "default" keyword argument to max() and min(), which provides a value to return in the event that an empty iterable is passed.
Could you write your proposal out in pure python so we can see how it interacts with the key-keyword argument and how it works when the number of positional arguments is not one.
Here's one option... I'm going to cheat a little here and just wrap the built-in min, but a quick/simple answer could be:
def min2(*vars, **kw): try: if 'key' in kw: return min(*vars, key=kw['key']) return min(*vars) except Exception: if 'default' in kw: return kw['default'] raise
This will swallow exceptions that are completely unrelated to the number of values we are taking the min() of. I am -1 on that. Here is my version. It also includes an 'initial' argument (not part of Adams proposal) to illustrate the difference between the two concepts. I would be +1 to adding 'initial' as well, but that is less interesting than 'default' because the 'initial' behavior is easy to get in other ways, e.g. "min(itertools.chain((initial,), values), key=func)". _marker = object() def min(*args, **kwargs): # extract and validate kwargs initial = kwargs.pop('initial', _marker) default = kwargs.pop('default', _marker) key = kwargs.pop('key', _marker) if kwargs: raise TypeError('min() got an unexpected keyword argument') # validate args, this TypeError is needed for backwards compatibility if initial is _marker and default is _marker and not args: raise TypeError('min expected 1 arguments, got 0') # create iterator for the values if len(args) == 1: it = iter(args[0]) else: it = iter(args) # extract first value if any and handle empty sequence if initial is not _marker: result = initial else: for result in it: break else: if default is _marker: raise ValueError('min() arg is an empty sequence') return default # handle remaining values if key is _marker: for value in it: if value < result: result = value else: resultkey = key(result) for value in it: valuekey = key(value) if valuekey < resultkey: result, resultkey = value, valuekey return result And here is what I get with the examples so far:
min() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 10, in min TypeError: min expected 1 arguments, got 0 min(default=0) 0 min(1,2,default=0) 1 min([1,2],default=0) 1 min([1,set()]) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 29, in min TypeError: can only compare to a set min([1,set()], default=7) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 29, in min TypeError: can only compare to a set def foo(): ... yield 1 ... raise ValueError ... min(foo()) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 28, in min File "<stdin>", line 3, in foo ValueError min(foo(), default=None) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 28, in min File "<stdin>", line 3, in foo ValueError
Cheers - Jacob