Skip to content

Commit 2b57bd0

Browse files
committed
allow initial as keyword, update docs and news
1 parent 6f26d49 commit 2b57bd0

File tree

6 files changed

+41
-8
lines changed

6 files changed

+41
-8
lines changed

Doc/library/functools.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,7 @@ The :mod:`functools` module defines the following functions:
453453
.. versionadded:: 3.4
454454

455455

456-
.. function:: reduce(function, iterable[, initial], /)
456+
.. function:: reduce(function, iterable, /[, initial])
457457

458458
Apply *function* of two arguments cumulatively to the items of *iterable*, from
459459
left to right, so as to reduce the iterable to a single value. For example,
@@ -468,7 +468,7 @@ The :mod:`functools` module defines the following functions:
468468

469469
initial_missing = object()
470470

471-
def reduce(function, iterable, initial=initial_missing, /):
471+
def reduce(function, iterable, /, initial=initial_missing):
472472
it = iter(iterable)
473473
if initial is initial_missing:
474474
value = next(it)
@@ -480,6 +480,8 @@ The :mod:`functools` module defines the following functions:
480480

481481
See :func:`itertools.accumulate` for an iterator that yields all intermediate
482482
values.
483+
.. versionchanged:: 3.14
484+
*initial* is now supported as a keyword argument.
483485

484486
.. decorator:: singledispatch
485487

Lib/functools.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,9 +234,9 @@ def __ge__(self, other):
234234

235235
_initial_missing = object()
236236

237-
def reduce(function, sequence, initial=_initial_missing):
237+
def reduce(function, sequence, /, initial=_initial_missing):
238238
"""
239-
reduce(function, iterable[, initial], /) -> value
239+
reduce(function, iterable, /[, initial]) -> value
240240
241241
Apply a function of two arguments cumulatively to the items of a sequence
242242
or iterable, from left to right, so as to reduce the iterable to a single

Lib/test/test_functools.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,6 +1005,29 @@ def __getitem__(self, i):
10051005
d = {"one": 1, "two": 2, "three": 3}
10061006
self.assertEqual(self.reduce(add, d), "".join(d.keys()))
10071007

1008+
# test correctness of keyword usage of `initial` in `reduce`
1009+
def test_initial_keyword(self):
1010+
def add(x, y):
1011+
return x + y
1012+
self.assertEqual(
1013+
self.reduce(add, ['a', 'b', 'c'], ''),
1014+
self.reduce(add, ['a', 'b', 'c'], initial=''),
1015+
)
1016+
self.assertEqual(
1017+
self.reduce(add, [['a', 'c'], [], ['d', 'w']], []),
1018+
self.reduce(add, [['a', 'c'], [], ['d', 'w']], initial=[]),
1019+
)
1020+
self.assertEqual(
1021+
self.reduce(lambda x, y: x*y, range(2,8), 1),
1022+
self.reduce(lambda x, y: x*y, range(2,8), initial=1),
1023+
)
1024+
self.assertEqual(
1025+
self.reduce(lambda x, y: x*y, range(2,21), 1),
1026+
self.reduce(lambda x, y: x*y, range(2,21), initial=1),
1027+
)
1028+
self.assertRaises(TypeError, self.reduce, add, [0, 1], initial="")
1029+
self.assertEqual(self.reduce(42, "", initial="1"), "1") # func is never called with one item
1030+
10081031

10091032
@unittest.skipUnless(c_functools, 'requires the C _functools module')
10101033
class TestReduceC(TestReduce, unittest.TestCase):

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,7 @@ Luke Dunstan
485485
Virgil Dupras
486486
Bruno Dupuis
487487
Andy Dustman
488+
Sayandip Dutta
488489
Gary Duzan
489490
Eugene Dvurechenski
490491
Karmen Dykstra
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Allow the *initial* argument of :func:`functools.reduce` to be a keyword.
2+
Patch by Sayandip Dutta.

Modules/_functoolsmodule.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -935,12 +935,16 @@ _functools_cmp_to_key_impl(PyObject *module, PyObject *mycmp)
935935
// Not converted to argument clinic, because of `args` in-place modification.
936936
// AC will affect performance.
937937
static PyObject *
938-
functools_reduce(PyObject *self, PyObject *args)
938+
functools_reduce(PyObject *self, PyObject *args, PyObject *kwargs)
939939
{
940940
PyObject *seq, *func, *result = NULL, *it;
941+
static char *keywords[] = {"", "", "initial", NULL};
941942

942-
if (!PyArg_UnpackTuple(args, "reduce", 2, 3, &func, &seq, &result))
943+
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|O:reduce", keywords,
944+
&func, &seq, &result)) {
943945
return NULL;
946+
}
947+
944948
if (result != NULL)
945949
Py_INCREF(result);
946950

@@ -1007,7 +1011,7 @@ functools_reduce(PyObject *self, PyObject *args)
10071011
}
10081012

10091013
PyDoc_STRVAR(functools_reduce_doc,
1010-
"reduce(function, iterable[, initial], /) -> value\n\
1014+
"reduce(function, iterable, /[, initial]) -> value\n\
10111015
\n\
10121016
Apply a function of two arguments cumulatively to the items of a sequence\n\
10131017
or iterable, from left to right, so as to reduce the iterable to a single\n\
@@ -1720,7 +1724,8 @@ PyDoc_STRVAR(_functools_doc,
17201724
"Tools that operate on functions.");
17211725

17221726
static PyMethodDef _functools_methods[] = {
1723-
{"reduce", functools_reduce, METH_VARARGS, functools_reduce_doc},
1727+
{"reduce", functools_reduce, METH_VARARGS|METH_KEYWORDS,
1728+
functools_reduce_doc},
17241729
_FUNCTOOLS_CMP_TO_KEY_METHODDEF
17251730
{NULL, NULL} /* sentinel */
17261731
};

0 commit comments

Comments
 (0)