Part of SF patch #1513870 (the still relevant part) -- add reduce() to

functools, and adjust docs etc.
This commit is contained in:
Guido van Rossum 2006-08-26 20:49:04 +00:00
parent 6a2a2a0832
commit 0919a1a07b
11 changed files with 168 additions and 78 deletions

View File

@ -289,19 +289,7 @@ There are also many useful builtin functions people seem not to be
aware of for some reason: \function{min()} and \function{max()} can
find the minimum/maximum of any sequence with comparable semantics,
for example, yet many people write their own
\function{max()}/\function{min()}. Another highly useful function is
\function{reduce()}. A classical use of \function{reduce()}
is something like
\begin{verbatim}
import sys, operator
nums = map(float, sys.argv[1:])
print reduce(operator.add, nums)/len(nums)
\end{verbatim}
This cute little script prints the average of all numbers given on the
command line. The \function{reduce()} adds up all the numbers, and
the rest is just some pre- and postprocessing.
\function{max()}/\function{min()}.
On the same note, note that \function{float()}, \function{int()} and
\function{long()} all accept arguments of type string, and so are

View File

@ -836,19 +836,6 @@ class Parrot(object):
\end{verbatim}
\end{funcdesc}
\begin{funcdesc}{reduce}{function, sequence\optional{, initializer}}
Apply \var{function} of two arguments cumulatively to the items of
\var{sequence}, from left to right, so as to reduce the sequence to
a single value. For example, \code{reduce(lambda x, y: x+y, [1, 2,
3, 4, 5])} calculates \code{((((1+2)+3)+4)+5)}. The left argument,
\var{x}, is the accumulated value and the right argument, \var{y},
is the update value from the \var{sequence}. If the optional
\var{initializer} is present, it is placed before the items of the
sequence in the calculation, and serves as a default when the
sequence is empty. If \var{initializer} is not given and
\var{sequence} contains only one item, the first item is returned.
\end{funcdesc}
\begin{funcdesc}{reload}{module}
Reload a previously imported \var{module}. The
argument must be a module object, so it must have been successfully
@ -1058,8 +1045,6 @@ class C:
The \var{sequence}'s items are normally numbers, and are not allowed
to be strings. The fast, correct way to concatenate sequence of
strings is by calling \code{''.join(\var{sequence})}.
Note that \code{sum(range(\var{n}), \var{m})} is equivalent to
\code{reduce(operator.add, range(\var{n}), \var{m})}
\versionadded{2.3}
\end{funcdesc}

View File

@ -51,6 +51,19 @@ two:
\end{verbatim}
\end{funcdesc}
\begin{funcdesc}{reduce}{function, sequence\optional{, initializer}}
Apply \var{function} of two arguments cumulatively to the items of
\var{sequence}, from left to right, so as to reduce the sequence to
a single value. For example, \code{reduce(lambda x, y: x+y, [1, 2,
3, 4, 5])} calculates \code{((((1+2)+3)+4)+5)}. The left argument,
\var{x}, is the accumulated value and the right argument, \var{y},
is the update value from the \var{sequence}. If the optional
\var{initializer} is present, it is placed before the items of the
sequence in the calculation, and serves as a default when the
sequence is empty. If \var{initializer} is not given and
\var{sequence} contains only one item, the first item is returned.
\end{funcdesc}
\begin{funcdesc}{update_wrapper}
{wrapper, wrapped\optional{, assigned}\optional{, updated}}
Update a wrapper function to look like the wrapped function. The optional

View File

@ -1893,8 +1893,8 @@ use \method{pop()} with \code{0} as the index. For example:
\subsection{Functional Programming Tools \label{functional}}
There are three built-in functions that are very useful when used with
lists: \function{filter()}, \function{map()}, and \function{reduce()}.
There are two built-in functions that are very useful when used with
lists: \function{filter()} and \function{map()}.
\samp{filter(\var{function}, \var{sequence})} returns a sequence
consisting of those items from the
@ -1934,42 +1934,6 @@ is shorter than another). For example:
>>> map(add, seq, seq)
[0, 2, 4, 6, 8, 10, 12, 14]
\end{verbatim}
\samp{reduce(\var{function}, \var{sequence})} returns a single value
constructed by calling the binary function \var{function} on the first two
items of the sequence, then on the result and the next item, and so
on. For example, to compute the sum of the numbers 1 through 10:
\begin{verbatim}
>>> def add(x,y): return x+y
...
>>> reduce(add, range(1, 11))
55
\end{verbatim}
If there's only one item in the sequence, its value is returned; if
the sequence is empty, an exception is raised.
A third argument can be passed to indicate the starting value. In this
case the starting value is returned for an empty sequence, and the
function is first applied to the starting value and the first sequence
item, then to the result and the next item, and so on. For example,
\begin{verbatim}
>>> def sum(seq):
... def add(x,y): return x+y
... return reduce(add, seq, 0)
...
>>> sum(range(1, 11))
55
>>> sum([])
0
\end{verbatim}
Don't use this example's definition of \function{sum()}: since summing
numbers is such a common need, a built-in function
\code{sum(\var{sequence})} is already provided, and works exactly like
this.
\versionadded{2.3}
\subsection{List Comprehensions}
@ -2739,7 +2703,7 @@ standard module \module{__builtin__}\refbimodindex{__builtin__}:
'id', 'int', 'intern', 'isinstance', 'issubclass', 'iter',
'len', 'license', 'list', 'locals', 'long', 'map', 'max', 'min',
'object', 'oct', 'open', 'ord', 'pow', 'property', 'quit', 'range',
'reduce', 'reload', 'repr', 'reversed', 'round', 'set',
'reload', 'repr', 'reversed', 'round', 'set',
'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super',
'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip']
\end{verbatim}

View File

@ -7,7 +7,7 @@
# Copyright (C) 2006 Python Software Foundation.
# See C source code for _functools credits/copyright
from _functools import partial
from _functools import partial, reduce
# update_wrapper() and wraps() are tools to help write
# wrapper functions that can handle naive introspection

View File

@ -259,6 +259,74 @@ class TestWraps(TestUpdateWrapper):
self.assertEqual(wrapper.attr, 'This is a different test')
self.assertEqual(wrapper.dict_attr, f.dict_attr)
class TestReduce(unittest.TestCase):
func = functools.reduce
def test_reduce(self):
class Squares:
def __init__(self, max):
self.max = max
self.sofar = []
def __len__(self):
return len(self.sofar)
def __getitem__(self, i):
if not 0 <= i < self.max: raise IndexError
n = len(self.sofar)
while n <= i:
self.sofar.append(n*n)
n += 1
return self.sofar[i]
self.assertEqual(self.func(lambda x, y: x+y, ['a', 'b', 'c'], ''), 'abc')
self.assertEqual(
self.func(lambda x, y: x+y, [['a', 'c'], [], ['d', 'w']], []),
['a','c','d','w']
)
self.assertEqual(self.func(lambda x, y: x*y, range(2,8), 1), 5040)
self.assertEqual(
self.func(lambda x, y: x*y, range(2,21), 1L),
2432902008176640000L
)
self.assertEqual(self.func(lambda x, y: x+y, Squares(10)), 285)
self.assertEqual(self.func(lambda x, y: x+y, Squares(10), 0), 285)
self.assertEqual(self.func(lambda x, y: x+y, Squares(0), 0), 0)
self.assertRaises(TypeError, self.func)
self.assertRaises(TypeError, self.func, 42, 42)
self.assertRaises(TypeError, self.func, 42, 42, 42)
self.assertEqual(self.func(42, "1"), "1") # func is never called with one item
self.assertEqual(self.func(42, "", "1"), "1") # func is never called with one item
self.assertRaises(TypeError, self.func, 42, (42, 42))
class BadSeq:
def __getitem__(self, index):
raise ValueError
self.assertRaises(ValueError, self.func, 42, BadSeq())
# Test reduce()'s use of iterators.
def test_iterator_usage(self):
class SequenceClass:
def __init__(self, n):
self.n = n
def __getitem__(self, i):
if 0 <= i < self.n:
return i
else:
raise IndexError
from operator import add
self.assertEqual(self.func(add, SequenceClass(5)), 10)
self.assertEqual(self.func(add, SequenceClass(5), 42), 52)
self.assertRaises(TypeError, self.func, add, SequenceClass(0))
self.assertEqual(self.func(add, SequenceClass(0), 42), 42)
self.assertEqual(self.func(add, SequenceClass(1)), 0)
self.assertEqual(self.func(add, SequenceClass(1), 42), 42)
d = {"one": 1, "two": 2, "three": 3}
self.assertEqual(self.func(add, d), "".join(d.keys()))
def test_main(verbose=None):
@ -268,7 +336,8 @@ def test_main(verbose=None):
TestPartialSubclass,
TestPythonPartial,
TestUpdateWrapper,
TestWraps
TestWraps,
TestReduce
)
test_support.run_unittest(*test_classes)

View File

@ -63,7 +63,7 @@ endif
if exists("python_highlight_builtins")
syn keyword pythonBuiltin unichr all set abs vars int __import__ unicode
syn keyword pythonBuiltin enumerate reduce exit issubclass
syn keyword pythonBuiltin enumerate exit issubclass
syn keyword pythonBuiltin divmod file Ellipsis isinstance open any
syn keyword pythonBuiltin locals help filter basestring slice copyright min
syn keyword pythonBuiltin super sum tuple hex execfile long id chr

View File

@ -901,7 +901,7 @@ lambda [param_list]: returnedExpr
-- Creates an anonymous function. returnedExpr must be
an expression, not a statement (e.g., not "if xx:...",
"print xxx", etc.) and thus can't contain newlines.
Used mostly for filter(), map(), reduce() functions, and GUI callbacks..
Used mostly for filter(), map() functions, and GUI callbacks..
List comprehensions
result = [expression for item1 in sequence1 [if condition1]
[for item2 in sequence2 ... for itemN in sequenceN]
@ -1005,11 +1005,6 @@ property() Created a property with access controlled by functions.
range(start [,end Returns list of ints from >= start and < end.With 1 arg,
[, step]]) list from 0..arg-1With 2 args, list from start..end-1With 3
args, list from start up to end by step
reduce(f, list [, Applies the binary function f to the items oflist so as to
init]) reduce the list to a single value.If init given, it is
"prepended" to list.
Re-parses and re-initializes an already imported module.
Useful in interactive mode, if you want to reload amodule
reload(module) after fixing it. If module was syntacticallycorrect but had
an error in initialization, mustimport it one more time
before calling reload().

View File

@ -386,7 +386,7 @@ support for features needed by `python-mode'.")
"isinstance" "issubclass" "iter" "len" "license"
"list" "locals" "long" "map" "max" "min" "object"
"oct" "open" "ord" "pow" "property" "range"
"reduce" "reload" "repr" "round"
"reload" "repr" "round"
"setattr" "slice" "staticmethod" "str" "sum"
"super" "tuple" "type" "unichr" "unicode" "vars"
"zip")

View File

@ -242,12 +242,88 @@ static PyTypeObject partial_type = {
};
/* reduce (used to be a builtin) ********************************************/
static PyObject *
functools_reduce(PyObject *self, PyObject *args)
{
PyObject *seq, *func, *result = NULL, *it;
if (!PyArg_UnpackTuple(args, "reduce", 2, 3, &func, &seq, &result))
return NULL;
if (result != NULL)
Py_INCREF(result);
it = PyObject_GetIter(seq);
if (it == NULL) {
PyErr_SetString(PyExc_TypeError,
"reduce() arg 2 must support iteration");
Py_XDECREF(result);
return NULL;
}
if ((args = PyTuple_New(2)) == NULL)
goto Fail;
for (;;) {
PyObject *op2;
if (args->ob_refcnt > 1) {
Py_DECREF(args);
if ((args = PyTuple_New(2)) == NULL)
goto Fail;
}
op2 = PyIter_Next(it);
if (op2 == NULL) {
if (PyErr_Occurred())
goto Fail;
break;
}
if (result == NULL)
result = op2;
else {
PyTuple_SetItem(args, 0, result);
PyTuple_SetItem(args, 1, op2);
if ((result = PyEval_CallObject(func, args)) == NULL)
goto Fail;
}
}
Py_DECREF(args);
if (result == NULL)
PyErr_SetString(PyExc_TypeError,
"reduce() of empty sequence with no initial value");
Py_DECREF(it);
return result;
Fail:
Py_XDECREF(args);
Py_XDECREF(result);
Py_DECREF(it);
return NULL;
}
PyDoc_STRVAR(functools_reduce_doc,
"reduce(function, sequence[, initial]) -> value\n\
\n\
Apply a function of two arguments cumulatively to the items of a sequence,\n\
from left to right, so as to reduce the sequence to a single value.\n\
For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates\n\
((((1+2)+3)+4)+5). If initial is present, it is placed before the items\n\
of the sequence in the calculation, and serves as a default when the\n\
sequence is empty.");
/* module level code ********************************************************/
PyDoc_STRVAR(module_doc,
"Tools that operate on functions.");
static PyMethodDef module_methods[] = {
{"reduce", functools_reduce, METH_VARARGS, functools_reduce_doc},
{NULL, NULL} /* sentinel */
};

View File

@ -3012,7 +3012,7 @@ init_ast(void)
if (PyDict_SetItemString(d, "AST", (PyObject*)AST_type) < 0) return;
if (PyModule_AddIntConstant(m, "PyCF_ONLY_AST", PyCF_ONLY_AST) < 0)
return;
if (PyModule_AddStringConstant(m, "__version__", "45597") < 0)
if (PyModule_AddStringConstant(m, "__version__", "51600") < 0)
return;
if (PyDict_SetItemString(d, "mod", (PyObject*)mod_type) < 0) return;
if (PyDict_SetItemString(d, "Module", (PyObject*)Module_type) < 0)