Issue 18111: Add a default argument to min() and max()

This commit is contained in:
Raymond Hettinger 2013-06-24 22:43:02 -07:00
parent d7a034bd75
commit 4d6018fe45
4 changed files with 78 additions and 28 deletions

View File

@ -753,19 +753,22 @@ are always available. They are listed here in alphabetical order.
already arranged into argument tuples, see :func:`itertools.starmap`\.
.. function:: max(iterable, *[, key])
.. function:: max(iterable, *[, default, key])
max(arg1, arg2, *args[, key])
Return the largest item in an iterable or the largest of two or more
arguments.
If one positional argument is provided, *iterable* must be a non-empty
iterable (such as a non-empty string, tuple or list). The largest item
in the iterable is returned. If two or more positional arguments are
provided, the largest of the positional arguments is returned.
If one positional argument is provided, it should be an :term:`iterable`.
The largest item in the iterable is returned. If two or more positional
arguments are provided, the smallest of the positional arguments is
returned.
The optional keyword-only *key* argument specifies a one-argument ordering
function like that used for :meth:`list.sort`.
There are two optional keyword-only arguments. The *key* argument specifies
a one-argument ordering function like that used for :meth:`list.sort`. The
*default* argument specifies an object to return if the provided iterable is
empty. If the iterable is empty and *default* is not provided, a
:exc:`ValueError` is raised.
If multiple items are maximal, the function returns the first one
encountered. This is consistent with other sort-stability preserving tools
@ -781,19 +784,22 @@ are always available. They are listed here in alphabetical order.
:ref:`typememoryview` for more information.
.. function:: min(iterable, *[, key])
.. function:: min(iterable, *[, default, key])
min(arg1, arg2, *args[, key])
Return the smallest item in an iterable or the smallest of two or more
arguments.
If one positional argument is provided, *iterable* must be a non-empty
iterable (such as a non-empty string, tuple or list). The smallest item
in the iterable is returned. If two or more positional arguments are
provided, the smallest of the positional arguments is returned.
If one positional argument is provided, it should be an :term:`iterable`.
The smallest item in the iterable is returned. If two or more positional
arguments are provided, the smallest of the positional arguments is
returned.
The optional keyword-only *key* argument specifies a one-argument ordering
function like that used for :meth:`list.sort`.
There are two optional keyword-only arguments. The *key* argument specifies
a one-argument ordering function like that used for :meth:`list.sort`. The
*default* argument specifies an object to return if the provided iterable is
empty. If the iterable is empty and *default* is not provided, a
:exc:`ValueError` is raised.
If multiple items are minimal, the function returns the first one
encountered. This is consistent with other sort-stability preserving tools

View File

@ -847,8 +847,19 @@ class BuiltinTest(unittest.TestCase):
self.assertEqual(max(1, 2.0, 3), 3)
self.assertEqual(max(1.0, 2, 3), 3)
self.assertRaises(TypeError, max)
self.assertRaises(TypeError, max, 42)
self.assertRaises(ValueError, max, ())
class BadSeq:
def __getitem__(self, index):
raise ValueError
self.assertRaises(ValueError, max, BadSeq())
for stmt in (
"max(key=int)", # no args
"max(default=None)",
"max(1, 2, default=None)", # require container for default
"max(default=None, key=int)",
"max(1, key=int)", # single arg not iterable
"max(1, 2, keystone=int)", # wrong keyword
"max(1, 2, key=int, abc=int)", # two many keywords
@ -865,6 +876,13 @@ class BuiltinTest(unittest.TestCase):
self.assertEqual(max((1,2), key=neg), 1) # two elem iterable
self.assertEqual(max(1, 2, key=neg), 1) # two elems
self.assertEqual(max((), default=None), None) # zero elem iterable
self.assertEqual(max((1,), default=None), 1) # one elem iterable
self.assertEqual(max((1,2), default=None), 2) # two elem iterable
self.assertEqual(max((), default=1, key=neg), 1)
self.assertEqual(max((1, 2), default=3, key=neg), 1)
data = [random.randrange(200) for i in range(100)]
keys = dict((elem, random.randrange(50)) for elem in data)
f = keys.__getitem__
@ -891,6 +909,9 @@ class BuiltinTest(unittest.TestCase):
for stmt in (
"min(key=int)", # no args
"min(default=None)",
"min(1, 2, default=None)", # require container for default
"min(default=None, key=int)",
"min(1, key=int)", # single arg not iterable
"min(1, 2, keystone=int)", # wrong keyword
"min(1, 2, key=int, abc=int)", # two many keywords
@ -907,6 +928,13 @@ class BuiltinTest(unittest.TestCase):
self.assertEqual(min((1,2), key=neg), 2) # two elem iterable
self.assertEqual(min(1, 2, key=neg), 2) # two elems
self.assertEqual(min((), default=None), None) # zero elem iterable
self.assertEqual(min((1,), default=None), 1) # one elem iterable
self.assertEqual(min((1,2), default=None), 1) # two elem iterable
self.assertEqual(min((), default=1, key=neg), 1)
self.assertEqual(min((1, 2), default=1, key=neg), 2)
data = [random.randrange(200) for i in range(100)]
keys = dict((elem, random.randrange(50)) for elem in data)
f = keys.__getitem__

View File

@ -13,6 +13,10 @@ Core and Builtins
- Issue #18184: PyUnicode_FromFormat() and PyUnicode_FromFormatV() now raise
OverflowError when an argument of %c format is out of range.
- Issue #18111: The min() and max() functions now support a default argument
to be returned instead of raising a ValueError on an empty sequence.
(Contributed by Julian Berman.)
- Issue #18137: Detect integer overflow on precision in float.__format__()
and complex.__format__().

View File

@ -1329,26 +1329,35 @@ static PyObject *
min_max(PyObject *args, PyObject *kwds, int op)
{
PyObject *v, *it, *item, *val, *maxitem, *maxval, *keyfunc=NULL;
PyObject *emptytuple, *defaultval = NULL;
static char *kwlist[] = {"key", "default", NULL};
const char *name = op == Py_LT ? "min" : "max";
const int positional = PyTuple_Size(args) > 1;
int ret;
if (PyTuple_Size(args) > 1)
if (positional)
v = args;
else if (!PyArg_UnpackTuple(args, (char *)name, 1, 1, &v))
return NULL;
if (kwds != NULL && PyDict_Check(kwds) && PyDict_Size(kwds)) {
keyfunc = PyDict_GetItemString(kwds, "key");
if (PyDict_Size(kwds)!=1 || keyfunc == NULL) {
PyErr_Format(PyExc_TypeError,
"%s() got an unexpected keyword argument", name);
return NULL;
}
Py_INCREF(keyfunc);
emptytuple = PyTuple_New(0);
if (emptytuple == NULL)
return NULL;
ret = PyArg_ParseTupleAndKeywords(emptytuple, kwds, "|$OO", kwlist,
&keyfunc, &defaultval);
Py_DECREF(emptytuple);
if (!ret)
return NULL;
if (positional && defaultval != NULL) {
PyErr_Format(PyExc_TypeError,
"Cannot specify a default for %s() with multiple "
"positional arguments", name);
return NULL;
}
it = PyObject_GetIter(v);
if (it == NULL) {
Py_XDECREF(keyfunc);
return NULL;
}
@ -1392,14 +1401,18 @@ min_max(PyObject *args, PyObject *kwds, int op)
if (PyErr_Occurred())
goto Fail_it;
if (maxval == NULL) {
PyErr_Format(PyExc_ValueError,
"%s() arg is an empty sequence", name);
assert(maxitem == NULL);
if (defaultval != NULL) {
Py_INCREF(defaultval);
maxitem = defaultval;
} else {
PyErr_Format(PyExc_ValueError,
"%s() arg is an empty sequence", name);
}
}
else
Py_DECREF(maxval);
Py_DECREF(it);
Py_XDECREF(keyfunc);
return maxitem;
Fail_it_item_and_val:
@ -1410,7 +1423,6 @@ Fail_it:
Py_XDECREF(maxval);
Py_XDECREF(maxitem);
Py_DECREF(it);
Py_XDECREF(keyfunc);
return NULL;
}