mirror of https://github.com/python/cpython
Issue 18111: Add a default argument to min() and max()
This commit is contained in:
parent
d7a034bd75
commit
4d6018fe45
|
@ -753,19 +753,22 @@ are always available. They are listed here in alphabetical order.
|
||||||
already arranged into argument tuples, see :func:`itertools.starmap`\.
|
already arranged into argument tuples, see :func:`itertools.starmap`\.
|
||||||
|
|
||||||
|
|
||||||
.. function:: max(iterable, *[, key])
|
.. function:: max(iterable, *[, default, key])
|
||||||
max(arg1, arg2, *args[, key])
|
max(arg1, arg2, *args[, key])
|
||||||
|
|
||||||
Return the largest item in an iterable or the largest of two or more
|
Return the largest item in an iterable or the largest of two or more
|
||||||
arguments.
|
arguments.
|
||||||
|
|
||||||
If one positional argument is provided, *iterable* must be a non-empty
|
If one positional argument is provided, it should be an :term:`iterable`.
|
||||||
iterable (such as a non-empty string, tuple or list). The largest item
|
The largest item in the iterable is returned. If two or more positional
|
||||||
in the iterable is returned. If two or more positional arguments are
|
arguments are provided, the smallest of the positional arguments is
|
||||||
provided, the largest of the positional arguments is returned.
|
returned.
|
||||||
|
|
||||||
The optional keyword-only *key* argument specifies a one-argument ordering
|
There are two optional keyword-only arguments. The *key* argument specifies
|
||||||
function like that used for :meth:`list.sort`.
|
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
|
If multiple items are maximal, the function returns the first one
|
||||||
encountered. This is consistent with other sort-stability preserving tools
|
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.
|
:ref:`typememoryview` for more information.
|
||||||
|
|
||||||
|
|
||||||
.. function:: min(iterable, *[, key])
|
.. function:: min(iterable, *[, default, key])
|
||||||
min(arg1, arg2, *args[, key])
|
min(arg1, arg2, *args[, key])
|
||||||
|
|
||||||
Return the smallest item in an iterable or the smallest of two or more
|
Return the smallest item in an iterable or the smallest of two or more
|
||||||
arguments.
|
arguments.
|
||||||
|
|
||||||
If one positional argument is provided, *iterable* must be a non-empty
|
If one positional argument is provided, it should be an :term:`iterable`.
|
||||||
iterable (such as a non-empty string, tuple or list). The smallest item
|
The smallest item in the iterable is returned. If two or more positional
|
||||||
in the iterable is returned. If two or more positional arguments are
|
arguments are provided, the smallest of the positional arguments is
|
||||||
provided, the smallest of the positional arguments is returned.
|
returned.
|
||||||
|
|
||||||
The optional keyword-only *key* argument specifies a one-argument ordering
|
There are two optional keyword-only arguments. The *key* argument specifies
|
||||||
function like that used for :meth:`list.sort`.
|
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
|
If multiple items are minimal, the function returns the first one
|
||||||
encountered. This is consistent with other sort-stability preserving tools
|
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, 2.0, 3), 3)
|
||||||
self.assertEqual(max(1.0, 2, 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 (
|
for stmt in (
|
||||||
"max(key=int)", # no args
|
"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, key=int)", # single arg not iterable
|
||||||
"max(1, 2, keystone=int)", # wrong keyword
|
"max(1, 2, keystone=int)", # wrong keyword
|
||||||
"max(1, 2, key=int, abc=int)", # two many keywords
|
"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 elem iterable
|
||||||
self.assertEqual(max(1, 2, key=neg), 1) # two elems
|
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)]
|
data = [random.randrange(200) for i in range(100)]
|
||||||
keys = dict((elem, random.randrange(50)) for elem in data)
|
keys = dict((elem, random.randrange(50)) for elem in data)
|
||||||
f = keys.__getitem__
|
f = keys.__getitem__
|
||||||
|
@ -891,6 +909,9 @@ class BuiltinTest(unittest.TestCase):
|
||||||
|
|
||||||
for stmt in (
|
for stmt in (
|
||||||
"min(key=int)", # no args
|
"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, key=int)", # single arg not iterable
|
||||||
"min(1, 2, keystone=int)", # wrong keyword
|
"min(1, 2, keystone=int)", # wrong keyword
|
||||||
"min(1, 2, key=int, abc=int)", # two many keywords
|
"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 elem iterable
|
||||||
self.assertEqual(min(1, 2, key=neg), 2) # two elems
|
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)]
|
data = [random.randrange(200) for i in range(100)]
|
||||||
keys = dict((elem, random.randrange(50)) for elem in data)
|
keys = dict((elem, random.randrange(50)) for elem in data)
|
||||||
f = keys.__getitem__
|
f = keys.__getitem__
|
||||||
|
|
|
@ -13,6 +13,10 @@ Core and Builtins
|
||||||
- Issue #18184: PyUnicode_FromFormat() and PyUnicode_FromFormatV() now raise
|
- Issue #18184: PyUnicode_FromFormat() and PyUnicode_FromFormatV() now raise
|
||||||
OverflowError when an argument of %c format is out of range.
|
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__()
|
- Issue #18137: Detect integer overflow on precision in float.__format__()
|
||||||
and complex.__format__().
|
and complex.__format__().
|
||||||
|
|
||||||
|
|
|
@ -1329,26 +1329,35 @@ static PyObject *
|
||||||
min_max(PyObject *args, PyObject *kwds, int op)
|
min_max(PyObject *args, PyObject *kwds, int op)
|
||||||
{
|
{
|
||||||
PyObject *v, *it, *item, *val, *maxitem, *maxval, *keyfunc=NULL;
|
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 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;
|
v = args;
|
||||||
else if (!PyArg_UnpackTuple(args, (char *)name, 1, 1, &v))
|
else if (!PyArg_UnpackTuple(args, (char *)name, 1, 1, &v))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (kwds != NULL && PyDict_Check(kwds) && PyDict_Size(kwds)) {
|
emptytuple = PyTuple_New(0);
|
||||||
keyfunc = PyDict_GetItemString(kwds, "key");
|
if (emptytuple == NULL)
|
||||||
if (PyDict_Size(kwds)!=1 || keyfunc == NULL) {
|
return NULL;
|
||||||
PyErr_Format(PyExc_TypeError,
|
ret = PyArg_ParseTupleAndKeywords(emptytuple, kwds, "|$OO", kwlist,
|
||||||
"%s() got an unexpected keyword argument", name);
|
&keyfunc, &defaultval);
|
||||||
return NULL;
|
Py_DECREF(emptytuple);
|
||||||
}
|
if (!ret)
|
||||||
Py_INCREF(keyfunc);
|
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);
|
it = PyObject_GetIter(v);
|
||||||
if (it == NULL) {
|
if (it == NULL) {
|
||||||
Py_XDECREF(keyfunc);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1392,14 +1401,18 @@ min_max(PyObject *args, PyObject *kwds, int op)
|
||||||
if (PyErr_Occurred())
|
if (PyErr_Occurred())
|
||||||
goto Fail_it;
|
goto Fail_it;
|
||||||
if (maxval == NULL) {
|
if (maxval == NULL) {
|
||||||
PyErr_Format(PyExc_ValueError,
|
|
||||||
"%s() arg is an empty sequence", name);
|
|
||||||
assert(maxitem == NULL);
|
assert(maxitem == NULL);
|
||||||
|
if (defaultval != NULL) {
|
||||||
|
Py_INCREF(defaultval);
|
||||||
|
maxitem = defaultval;
|
||||||
|
} else {
|
||||||
|
PyErr_Format(PyExc_ValueError,
|
||||||
|
"%s() arg is an empty sequence", name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
Py_DECREF(maxval);
|
Py_DECREF(maxval);
|
||||||
Py_DECREF(it);
|
Py_DECREF(it);
|
||||||
Py_XDECREF(keyfunc);
|
|
||||||
return maxitem;
|
return maxitem;
|
||||||
|
|
||||||
Fail_it_item_and_val:
|
Fail_it_item_and_val:
|
||||||
|
@ -1410,7 +1423,6 @@ Fail_it:
|
||||||
Py_XDECREF(maxval);
|
Py_XDECREF(maxval);
|
||||||
Py_XDECREF(maxitem);
|
Py_XDECREF(maxitem);
|
||||||
Py_DECREF(it);
|
Py_DECREF(it);
|
||||||
Py_XDECREF(keyfunc);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue