Commit 4d6018fe authored by Raymond Hettinger's avatar Raymond Hettinger

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

parent d7a034bd
......@@ -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
......
......@@ -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__
......
......@@ -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__().
......
......@@ -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) {
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,
"%s() got an unexpected keyword argument", name);
"Cannot specify a default for %s() with multiple "
"positional arguments", name);
return NULL;
}
Py_INCREF(keyfunc);
}
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) {
assert(maxitem == NULL);
if (defaultval != NULL) {
Py_INCREF(defaultval);
maxitem = defaultval;
} else {
PyErr_Format(PyExc_ValueError,
"%s() arg is an empty sequence", name);
assert(maxitem == NULL);
}
}
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;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment