summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/functions.rst34
-rw-r--r--Lib/test/test_builtin.py28
-rw-r--r--Misc/NEWS4
-rw-r--r--Python/bltinmodule.c40
4 files changed, 78 insertions, 28 deletions
diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst
index 43164af..3d239ec 100644
--- a/Doc/library/functions.rst
+++ b/Doc/library/functions.rst
@@ -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
diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py
index f850456..acbc29c 100644
--- a/Lib/test/test_builtin.py
+++ b/Lib/test/test_builtin.py
@@ -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__
diff --git a/Misc/NEWS b/Misc/NEWS
index 3ea542a..bc825dc 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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__().
diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c
index 356bb50..86e0805 100644
--- a/Python/bltinmodule.c
+++ b/Python/bltinmodule.c
@@ -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;
}