[Python-ideas] 'default' keyword argument for max(), min()
Jacob Holm
jh at improva.dk
Thu Apr 16 12:33:05 CEST 2009
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
More information about the Python-ideas
mailing list