
Hi, First a brief introduction, since this is my first post to python-dev: I'm a software engineer, mostly using C/C++ and Python. I also occasionaly teach a tutorial class on Python. A couple weeks ago I told Raymond Hettinger how I liked itertools and that I always missed a feature from map that could be easily implemeted in imap. He told me that he was trying to stay as close as possible to the behavior of the original map function. This why I am proposing this backward compatible enhancement to map: Using map you always end up using lambda functions to pass constant arguments to the mapping function like this: map( lambda x,y=K: f(x,y), L ) What I propose in this patch is to have map recognize its keyword arguments like this: map( f, L , y=K ) The result would be: def map( f, L, **kw ): # accepts only one list for simplicity l=[] for i in L: l.append( f(i,**kw) ) return l While it doesn't solve every problem (passing a value to the first arg) it could help remove many uses of lambda functions. If it turns out to be useful to people other than me I can also make a patch for the filter function which could benefit from the same extension. Since the patch is very small I include it here (against the latest python 2.3 cvs) Comments ? Index: src/Doc/lib/libfuncs.tex =================================================================== RCS file: /cvsroot/python/python/dist/src/Doc/lib/libfuncs.tex,v retrieving revision 1.128 diff -u -r1.128 libfuncs.tex --- src/Doc/lib/libfuncs.tex 31 Jan 2003 17:19:05 -0000 1.128 +++ src/Doc/lib/libfuncs.tex 18 Feb 2003 10:48:15 -0000 @@ -643,12 +643,14 @@ point numbers to integers truncates (towards zero). \end{funcdesc} -\begin{funcdesc}{map}{function, list, ...} +\begin{funcdesc}{map}{function, list1, ..., listN\optional{, k1=a1,...}} Apply \var{function} to every item of \var{list} and return a list of the results. If additional \var{list} arguments are passed, \var{function} must take that many arguments and is applied to the items of all lists in parallel; if a list is shorter than another it - is assumed to be extended with \code{None} items. If \var{function} + is assumed to be extended with \code{None} items. If optional keyword + arguments are passed, \var{function} must accept them as named arguments + and they are passed each time \var{function} is called. If \var{function} is \code{None}, the identity function is assumed; if there are multiple list arguments, \function{map()} returns a list consisting of tuples containing the corresponding items from all lists (a kind Index: src/Python/bltinmodule.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Python/bltinmodule.c,v retrieving revision 2.281 diff -u -r2.281 bltinmodule.c --- src/Python/bltinmodule.c 13 Feb 2003 22:07:58 -0000 2.281 +++ src/Python/bltinmodule.c 18 Feb 2003 10:48:22 -0000 @@ -731,7 +731,7 @@ static PyObject * -builtin_map(PyObject *self, PyObject *args) +builtin_map(PyObject *self, PyObject *args, PyObject *kwargs) { typedef struct { PyObject *it; /* the iterator object */ @@ -854,7 +854,7 @@ if (func == Py_None) value = alist; else { - value = PyEval_CallObject(func, alist); + value = PyEval_CallObjectWithKeywords(func, alist, kwargs); Py_DECREF(alist); if (value == NULL) goto Fail_1; @@ -1780,7 +1780,7 @@ {"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}, + {"map", builtin_map, METH_KEYWORDS, map_doc}, {"max", builtin_max, METH_VARARGS, max_doc}, {"min", builtin_min, METH_VARARGS, min_doc}, {"oct", builtin_oct, METH_O, oct_doc}, -- Ludovic Aubry LOGILAB, Paris (France). http://www.logilab.com http://www.logilab.fr http://www.logilab.org

Using map you always end up using lambda functions to pass constant arguments to the mapping function like this:
map( lambda x,y=K: f(x,y), L )
This can now done by writing instead: map(lambda x: f(x, K), L) or if you don't want the lambda, you can use a list comprehension: [f(x, K) for x in L]
What I propose in this patch is to have map recognize its keyword arguments like this:
map( f, L , y=K )
The result would be: def map( f, L, **kw ): # accepts only one list for simplicity l=[] for i in L: l.append( f(i,**kw) ) return l
While it doesn't solve every problem (passing a value to the first arg) it could help remove many uses of lambda functions.
While your patch scores high in minimal changes to the implementation, I don't like the resulting notation. While map(f, L) is reasonably natural, map(f, L, y=K) is not. Also, most C functions don't accept keyword arguments, so this won't help as much in the case where there's a speed reason for avoiding lambdas and list comprehensions. --Guido van Rossum (home page: http://www.python.org/~guido/)

[posted and mailed] Ludovic Aubry <Ludovic.Aubry@logilab.fr> wrote in news:20030218111844.GA10112@logilab.fr:
Using map you always end up using lambda functions to pass constant arguments to the mapping function like this:
map( lambda x,y=K: f(x,y), L )
You can also write this as: map(lambda x: f(x,K), L) which is arguably cleaner although these days I would probably use a list comprehension.
What I propose in this patch is to have map recognize its keyword arguments like this:
map( f, L , y=K )
The result would be: def map( f, L, **kw ): # accepts only one list for simplicity l=[] for i in L: l.append( f(i,**kw) ) return l
Or in the general case: def map(f, *L, **kw): return [ f(*i, **kw) for i in L]
While it doesn't solve every problem (passing a value to the first arg) it could help remove many uses of lambda functions.
Have you looked at any code using map to see what proportion of calls this would help. In particular how often do all the fixed arguments match this pattern so they could be passed as keywords? I do have a problem with this. It looks as though it ought to work in some situations where it doesn't. For example, searching my lib directory for occurences of map, I only found one place where it looks as though this new form could be used. That is in win32comext\mapi\mapiutil.py which contains: return map(lambda(v): v[1], data) It appears as though you ought to be able to write this as: return map(operator.getitem, data, b=1) except that operator.getitem, in common with many builtin functions doesn't accept any keyword arguments. This is a pity as it cripples most of the situations where you might otherwise want to use this. e.g. add a constant to elements of a list, or multiply by a constant. -- Duncan Booth duncan@rcp.co.uk int month(char *p){return(124864/((p[0]+p[1]-p[2]&0x1f)+1)%12)["\5\x8\3" "\6\7\xb\1\x9\xa\2\0\4"];} // Who said my code was obscure?

It appears as though you ought to be able to write this as:
return map(operator.getitem, data, b=1)
except that operator.getitem, in common with many builtin functions doesn't accept any keyword arguments.
This is a pity as it cripples most of the situations where you might otherwise want to use this. e.g. add a constant to elements of a list, or multiply by a constant.
For Py2.3, the itertools module can help: itertools.imap(operator.__getitem__, data, itertools.repeat(1)) Raymond Hettinger

This is nice, The strongest objection regarding using map(f,L,x=K) is that there is no clear way of telling the reader that the keyword argument will be passed as a constant Your solution for itertools can work with map too: def repeat(v,n): while n: yield v n-=1 map(f,L,repeat(K,len(L))) would do the trick As a nice addition it also allows one to put constants anywhere in the argument list for f Thanks for the idea On Tue, Feb 18, 2003 at 10:13:44PM -0500, Raymond Hettinger wrote:
It appears as though you ought to be able to write this as:
return map(operator.getitem, data, b=1)
except that operator.getitem, in common with many builtin functions doesn't accept any keyword arguments.
This is a pity as it cripples most of the situations where you might otherwise want to use this. e.g. add a constant to elements of a list, or multiply by a constant.
For Py2.3, the itertools module can help:
itertools.imap(operator.__getitem__, data, itertools.repeat(1))
Raymond Hettinger
_______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev
-- Ludovic Aubry LOGILAB, Paris (France). http://www.logilab.com http://www.logilab.fr http://www.logilab.org

Your solution for itertools can work with map too:
def repeat(v,n): while n: yield v n-=1
map(f,L,repeat(K,len(L)))
The itertools combine together so you can do it all at C speed and not have to create your own ad-hoc generator: from itertools import repeat, islice map(f, L, islice(repeat(K),len(L))) Raymond Hettinger
participants (4)
-
Duncan Booth
-
Guido van Rossum
-
Ludovic Aubry
-
Raymond Hettinger