36

I know, that

map(function, arguments)

is equivalent to

for argument in arguments:
    function(argument)

Is it possible to use map function to do the following?

for arg, kwargs in arguments:
    function(arg, **kwargs)
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Radosław Miernik
  • 4,004
  • 8
  • 33
  • 36
  • 1
    Those two things are not equivalent. `map()` builds a list (or in 3.x, a generator) of the returned values from the function calls. If you don't want to use those values, don't use `map()`, just use a normal loop. – Gareth Latty Jun 01 '13 at 15:32
  • 9
    `map` is a function (that returns a list), and the loop is code with no return value. They're not equivalent. – Paul Hankin Jun 01 '13 at 15:32

4 Answers4

37

You can with a lambda:

map(lambda a: function(a[0], **a[1]), arguments)

or you could use a generator expression or list comprehension, depending on what you want:

(function(a, **k) for a, k in arguments)
[function(a, **k) for a, k in arguments]

In Python 2, map() returns a list (so the list comprehension is the equivalent), in Python 3, map() is a generator (so the generator expression can replace it).

There is no built-in or standard library method that does this directly; the use case is too specialised.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
24

For the case of positional arguments only, you can use itertools.starmap(fun, args):

Return an iterator whose values are returned from the function evaluated with a argument tuple taken from the given sequence.

Example:

from itertools import starmap

def f(i, arg):
    print(arg * (i+1))

for _ in starmap(f, enumerate(["a", "b", "c"])):
    pass

prints:

a
bb
ccc
eddygeek
  • 4,236
  • 3
  • 25
  • 32
3

You just have to remember that map will pass the arguments to the function as one tuple rather than separate arguments. If you can't change your original function, you can call it with a helper function:

def f(tup):
    args, kwargs = tup
    function(args, **kwargs)

map(f, arguments)
DJG
  • 6,413
  • 4
  • 30
  • 51
1

I kept running into the same need and ended up making the following function:

def kwarg_map(element_constructor, **kwarg_lists):
    """
    A helper function for when you want to construct a chain of objects with individual arguments for each one.  Can
    be easier to read than a list expansion.

    :param element_constructor: A function of the form object = fcn(**kwargs)
    :param kwarg_lists: A dict of lists, where the index identifies two which element its corresponding value will go.
    :return: A list of objects.

    e.g. Initializing a chain of layers:
        layer_sizes = [784, 240, 240, 10]
        layers = kwarg_map(
            Layer,
            n_in = layer_sizes[:-1],
            n_out = layer_sizes[1:],
            activation = ['tanh', 'tanh', 'softmax'],
            )

    is equivalent to:
        layers = [Layer(n_in=784, n_out=240, activation='tanh'), Layer(n_in=240, n_out=240, activation='tanh'), Layer(n_in=240, n_out=10, activation='softmax')]
    """
    all_lens = [len(v) for v in kwarg_lists.values()]
    assert len(kwarg_lists)>0, "You need to specify at least list of arguments (otherwise you don't need this function)"
    n_elements = all_lens[0]
    assert all(n_elements == le for le in all_lens), 'Inconsistent lengths: %s' % (all_lens, )
    return [element_constructor(**{k: v[i] for k, v in kwarg_lists.iteritems()}) for i in xrange(n_elements)]
Peter
  • 12,274
  • 9
  • 71
  • 86