From a6c6037f882573c9965e4fac74765357e42148e4 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Thu, 13 Mar 2008 01:26:19 +0000 Subject: Issues 2186 and 2187. Move map() from itertools to builtins. --- Doc/library/itertools.rst | 37 +++-------- Lib/filecmp.py | 6 +- Lib/test/seq_tests.py | 4 +- Lib/test/test_heapq.py | 4 +- Lib/test/test_itertools.py | 1 + Lib/test/test_set.py | 4 +- Modules/itertoolsmodule.c | 151 -------------------------------------------- Python/bltinmodule.c | 154 ++++++++++++++++++++++++++++++++++++++++----- 8 files changed, 158 insertions(+), 203 deletions(-) diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index 368e40d..032c0b8 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -22,9 +22,8 @@ The tools are designed to combine readily with one another. This makes it easy to construct more specialized tools succinctly and efficiently in pure Python. For instance, SML provides a tabulation tool: ``tabulate(f)`` which produces a -sequence ``f(0), f(1), ...``. This toolbox provides :func:`imap` and -:func:`count` which can be combined to form ``imap(f, count())`` and produce an -equivalent result. +sequence ``f(0), f(1), ...``. But, this effect can be achieved in Python +by combining :func:`map` and :func:`count` to form ``map(f, count())``. Likewise, the functional tools are designed to work well with the high-speed functions provided by the :mod:`operator` module. @@ -138,7 +137,7 @@ loops that truncate the stream. .. function:: count([n]) Make an iterator that returns consecutive integers starting with *n*. If not - specified *n* defaults to zero. Often used as an argument to :func:`imap` to + specified *n* defaults to zero. Often used as an argument to :func:`map` to generate consecutive data points. Also, used with :func:`izip` to add sequence numbers. Equivalent to:: @@ -248,22 +247,6 @@ loops that truncate the stream. yield x -.. function:: imap(function, *iterables) - - Make an iterator that computes the function using arguments from each of the - iterables. This function is the same as the built-in :func:`map` function. - Equivalent to:: - - def imap(function, *iterables): - iterables = [iter(it) for it in iterables) - while True: - args = [next(it) for it in iterables] - if function is None: - yield tuple(args) - else: - yield function(*args) - - .. function:: islice(iterable, [start,] stop [, step]) Make an iterator that returns selected elements from the iterable. If *start* is @@ -421,7 +404,7 @@ loops that truncate the stream. .. function:: repeat(object[, times]) Make an iterator that returns *object* over and over again. Runs indefinitely - unless the *times* argument is specified. Used as argument to :func:`imap` for + unless the *times* argument is specified. Used as argument to :func:`map` for invariant parameters to the called function. Also used with :func:`izip` to create an invariant part of a tuple record. Equivalent to:: @@ -437,9 +420,9 @@ loops that truncate the stream. .. function:: starmap(function, iterable) Make an iterator that computes the function using arguments obtained from - the iterable. Used instead of :func:`imap` when argument parameters are already + the iterable. Used instead of :func:`map` when argument parameters are already grouped in tuples from a single iterable (the data has been "pre-zipped"). The - difference between :func:`imap` and :func:`starmap` parallels the distinction + difference between :func:`map` and :func:`starmap` parallels the distinction between ``function(a,b)`` and ``function(*c)``. Equivalent to:: def starmap(function, iterable): @@ -507,7 +490,7 @@ can be combined. :: Check 1202 is for $823.14 >>> import operator - >>> for cube in imap(operator.pow, range(1,5), repeat(3)): + >>> for cube in map(operator.pow, range(1,5), repeat(3)): ... print(cube) ... 1 @@ -577,7 +560,7 @@ which incur interpreter overhead. :: def tabulate(function): "Return function(0), function(1), ..." - return imap(function, count()) + return map(function, count()) def nth(iterable, n): "Returns the nth item or raise StopIteration" @@ -603,7 +586,7 @@ which incur interpreter overhead. :: def quantify(seq, pred=None): "Count how many times the predicate is true in the sequence" - return sum(imap(pred, seq)) + return sum(map(pred, seq)) def padnone(seq): """Returns the sequence elements and then returns None indefinitely. @@ -617,7 +600,7 @@ which incur interpreter overhead. :: return chain.from_iterable(repeat(seq, n)) def dotproduct(vec1, vec2): - return sum(imap(operator.mul, vec1, vec2)) + return sum(map(operator.mul, vec1, vec2)) def flatten(listOfLists): return list(chain.from_iterable(listOfLists)) diff --git a/Lib/filecmp.py b/Lib/filecmp.py index 14e1eb4..00fc135 100644 --- a/Lib/filecmp.py +++ b/Lib/filecmp.py @@ -12,7 +12,7 @@ Functions: import os import stat import warnings -from itertools import ifilterfalse, imap, izip +from itertools import ifilterfalse, izip __all__ = ["cmp","dircmp","cmpfiles"] @@ -130,8 +130,8 @@ class dircmp: self.right_list.sort() def phase1(self): # Compute common names - a = dict(izip(imap(os.path.normcase, self.left_list), self.left_list)) - b = dict(izip(imap(os.path.normcase, self.right_list), self.right_list)) + a = dict(izip(map(os.path.normcase, self.left_list), self.left_list)) + b = dict(izip(map(os.path.normcase, self.right_list), self.right_list)) self.common = list(map(a.__getitem__, filter(b.__contains__, a))) self.left_only = list(map(a.__getitem__, ifilterfalse(b.__contains__, a))) self.right_only = list(map(b.__getitem__, ifilterfalse(a.__contains__, b))) diff --git a/Lib/test/seq_tests.py b/Lib/test/seq_tests.py index a3815e2..dfa18c5 100644 --- a/Lib/test/seq_tests.py +++ b/Lib/test/seq_tests.py @@ -79,10 +79,10 @@ class IterFuncStop: def __next__(self): raise StopIteration -from itertools import chain, imap +from itertools import chain, map def itermulti(seqn): 'Test multiple tiers of iterators' - return chain(imap(lambda x:x, iterfunc(IterGen(Sequence(seqn))))) + return chain(map(lambda x:x, iterfunc(IterGen(Sequence(seqn))))) class CommonTest(unittest.TestCase): # The type to be tested diff --git a/Lib/test/test_heapq.py b/Lib/test/test_heapq.py index 8ae1637..7969d6e 100644 --- a/Lib/test/test_heapq.py +++ b/Lib/test/test_heapq.py @@ -260,10 +260,10 @@ class S: def __next__(self): raise StopIteration -from itertools import chain, imap +from itertools import chain, map def L(seqn): 'Test multiple tiers of iterators' - return chain(imap(lambda x:x, R(Ig(G(seqn))))) + return chain(map(lambda x:x, R(Ig(G(seqn))))) class TestErrorHandling(unittest.TestCase): # only for C implementation diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py index 13dc1f9..7d905d8 100644 --- a/Lib/test/test_itertools.py +++ b/Lib/test/test_itertools.py @@ -9,6 +9,7 @@ from functools import reduce maxsize = test_support.MAX_Py_ssize_t minsize = -maxsize-1 ifilter = filter +imap = map def lzip(*args): return list(zip(*args)) diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py index 9e3d64f..ca3c02e 100644 --- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -1560,10 +1560,10 @@ class S: def __next__(self): raise StopIteration -from itertools import chain, imap +from itertools import chain def L(seqn): 'Test multiple tiers of iterators' - return chain(imap(lambda x:x, R(Ig(G(seqn))))) + return chain(map(lambda x:x, R(Ig(G(seqn))))) class TestVariousIteratorArgs(unittest.TestCase): diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index 1f2a43b..f1d33cd 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -1418,155 +1418,6 @@ static PyTypeObject starmap_type = { }; -/* imap object ************************************************************/ - -typedef struct { - PyObject_HEAD - PyObject *iters; - PyObject *func; -} imapobject; - -static PyTypeObject imap_type; - -static PyObject * -imap_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyObject *it, *iters, *func; - imapobject *lz; - Py_ssize_t numargs, i; - - if (type == &imap_type && !_PyArg_NoKeywords("imap()", kwds)) - return NULL; - - numargs = PyTuple_Size(args); - if (numargs < 2) { - PyErr_SetString(PyExc_TypeError, - "imap() must have at least two arguments."); - return NULL; - } - - iters = PyTuple_New(numargs-1); - if (iters == NULL) - return NULL; - - for (i=1 ; itp_alloc(type, 0); - if (lz == NULL) { - Py_DECREF(iters); - return NULL; - } - lz->iters = iters; - func = PyTuple_GET_ITEM(args, 0); - Py_INCREF(func); - lz->func = func; - - return (PyObject *)lz; -} - -static void -imap_dealloc(imapobject *lz) -{ - PyObject_GC_UnTrack(lz); - Py_XDECREF(lz->iters); - Py_XDECREF(lz->func); - Py_TYPE(lz)->tp_free(lz); -} - -static int -imap_traverse(imapobject *lz, visitproc visit, void *arg) -{ - Py_VISIT(lz->iters); - Py_VISIT(lz->func); - return 0; -} - -static PyObject * -imap_next(imapobject *lz) -{ - PyObject *val; - PyObject *argtuple; - PyObject *result; - Py_ssize_t numargs, i; - - numargs = PyTuple_Size(lz->iters); - argtuple = PyTuple_New(numargs); - if (argtuple == NULL) - return NULL; - - for (i=0 ; iiters, i)); - if (val == NULL) { - Py_DECREF(argtuple); - return NULL; - } - PyTuple_SET_ITEM(argtuple, i, val); - } - result = PyObject_Call(lz->func, argtuple, NULL); - Py_DECREF(argtuple); - return result; -} - -PyDoc_STRVAR(imap_doc, -"imap(func, *iterables) --> imap object\n\ -\n\ -Make an iterator that computes the function using arguments from\n\ -each of the iterables. Stops when the shortest iterable is exhausted."); - -static PyTypeObject imap_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "itertools.imap", /* tp_name */ - sizeof(imapobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)imap_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, /* tp_flags */ - imap_doc, /* tp_doc */ - (traverseproc)imap_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)imap_next, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - imap_new, /* tp_new */ - PyObject_GC_Del, /* tp_free */ -}; - - /* chain object ************************************************************/ typedef struct { @@ -3068,7 +2919,6 @@ izip_longest(p, q, ...) --> (p[0], q[0]), (p[1], q[1]), ... \n\ ifilterfalse(pred, seq) --> elements of seq where pred(elem) is False\n\ islice(seq, [start,] stop [, step]) --> elements from\n\ seq[start:stop:step]\n\ -imap(fun, p, q, ...) --> fun(p0, q0), fun(p1, q1), ...\n\ starmap(fun, seq) --> fun(*seq[0]), fun(*seq[1]), ...\n\ tee(it, n=2) --> (it1, it2 , ... itn) splits one iterator into n\n\ chain(p, q, ...) --> p0, p1, ... plast, q0, q1, ... \n\ @@ -3096,7 +2946,6 @@ inititertools(void) &takewhile_type, &islice_type, &starmap_type, - &imap_type, &chain_type, &ifilterfalse_type, &count_type, diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 7baaf14..024729e 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -864,31 +864,153 @@ Return the identity of an object. This is guaranteed to be unique among\n\ simultaneously existing objects. (Hint: it's the object's memory address.)"); +/* map object ************************************************************/ + +typedef struct { + PyObject_HEAD + PyObject *iters; + PyObject *func; +} mapobject; + +PyTypeObject PyMap_Type; + static PyObject * -builtin_map(PyObject *self, PyObject *args) +map_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { - PyObject *itertools, *imap, *result; - itertools = PyImport_ImportModuleNoBlock("itertools"); - if (itertools == NULL) + PyObject *it, *iters, *func; + mapobject *lz; + Py_ssize_t numargs, i; + + if (type == &PyMap_Type && !_PyArg_NoKeywords("map()", kwds)) return NULL; - imap = PyObject_GetAttrString(itertools, "imap"); - Py_DECREF(itertools); - if (imap == NULL) + + numargs = PyTuple_Size(args); + if (numargs < 2) { + PyErr_SetString(PyExc_TypeError, + "map() must have at least two arguments."); + return NULL; + } + + iters = PyTuple_New(numargs-1); + if (iters == NULL) + return NULL; + + for (i=1 ; itp_alloc(type, 0); + if (lz == NULL) { + Py_DECREF(iters); + return NULL; + } + lz->iters = iters; + func = PyTuple_GET_ITEM(args, 0); + Py_INCREF(func); + lz->func = func; + + return (PyObject *)lz; +} + +static void +map_dealloc(mapobject *lz) +{ + PyObject_GC_UnTrack(lz); + Py_XDECREF(lz->iters); + Py_XDECREF(lz->func); + Py_TYPE(lz)->tp_free(lz); +} + +static int +map_traverse(mapobject *lz, visitproc visit, void *arg) +{ + Py_VISIT(lz->iters); + Py_VISIT(lz->func); + return 0; +} + +static PyObject * +map_next(mapobject *lz) +{ + PyObject *val; + PyObject *argtuple; + PyObject *result; + Py_ssize_t numargs, i; + + numargs = PyTuple_Size(lz->iters); + argtuple = PyTuple_New(numargs); + if (argtuple == NULL) return NULL; - result = PyObject_Call(imap, args, NULL); - Py_DECREF(imap); + + for (i=0 ; iiters, i)); + if (val == NULL) { + Py_DECREF(argtuple); + return NULL; + } + PyTuple_SET_ITEM(argtuple, i, val); + } + result = PyObject_Call(lz->func, argtuple, NULL); + Py_DECREF(argtuple); return result; } PyDoc_STRVAR(map_doc, -"map(function, iterable[, iterable, ...]) -> iterator\n\ +"map(func, *iterables) --> map object\n\ \n\ -Return an iterator yielding the results of applying the function to the\n\ -items of the argument iterables(s). If more than one iterable is given,\n\ -the function is called with an argument list consisting of the\n\ -corresponding item of each iterable, until an iterable is exhausted.\n\ -(This is identical to itertools.imap().)"); +Make an iterator that computes the function using arguments from\n\ +each of the iterables. Stops when the shortest iterable is exhausted."); +PyTypeObject PyMap_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "map", /* tp_name */ + sizeof(mapobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)map_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + map_doc, /* tp_doc */ + (traverseproc)map_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)map_next, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + map_new, /* tp_new */ + PyObject_GC_Del, /* tp_free */ +}; static PyObject * builtin_next(PyObject *self, PyObject *args) @@ -1893,7 +2015,6 @@ static PyMethodDef builtin_methods[] = { {"iter", builtin_iter, METH_VARARGS, iter_doc}, {"len", builtin_len, METH_O, len_doc}, {"locals", (PyCFunction)builtin_locals, METH_NOARGS, locals_doc}, - {"map", builtin_map, METH_VARARGS, map_doc}, {"max", (PyCFunction)builtin_max, METH_VARARGS | METH_KEYWORDS, max_doc}, {"min", (PyCFunction)builtin_min, METH_VARARGS | METH_KEYWORDS, min_doc}, {"next", (PyCFunction)builtin_next, METH_VARARGS, next_doc}, @@ -1965,6 +2086,7 @@ _PyBuiltin_Init(void) SETBUILTIN("property", &PyProperty_Type); SETBUILTIN("int", &PyLong_Type); SETBUILTIN("list", &PyList_Type); + SETBUILTIN("map", &PyMap_Type); SETBUILTIN("object", &PyBaseObject_Type); SETBUILTIN("range", &PyRange_Type); SETBUILTIN("reversed", &PyReversed_Type); -- cgit v0.12