How to use map() to call class methods on a list of objects

Question:

I am trying to call object.method() on a list of objects.

I have tried this but can’t get it to work properly

newList = map(method, objectList)

I get the error method is not defined but I know that is because it is a class method and not a local function.

Is there a way to do this with map(), or a similar built in function? Or will I have to use a generator/list comprehension?

edit Could you also explain the advantages or contrast your solution to using this list comprehension?

newList = [object.method() for object in objectList]
Asked By: Stephan

||

Answers:

If the contents of the list are all instances of the same class, you can prefix the method name with the class name.

class Fred:
    def __init__(self, val):
        self.val = val
    def frob(self):
        return self.val

freds = [Fred(4), Fred(8), Fred(15)]
print map(Fred.frob, freds)

Result:

[4, 8, 15]

This can also be done if the elements of the list are subclasses of the specified class. However, it will still call the specified implementation of the method, even if that method is overridden in the subclass. Example:

class Fred:
    def __init__(self, val):
        self.val = val
    def frob(self):
        return self.val

class Barney(Fred):
    def frob(self):
        return self.val * 2

freds = [Fred(4), Barney(8), Barney(15)]
#You might expect the barneys to return twice their val. ex. [4, 16, 30]
#but the actual output is [4, 8, 15]
print map(Fred.frob, freds)
Answered By: Kevin

Use operator.methodcaller():

from operator import methodcaller

map(methodcaller('methodname'), object_list)

This works for any list of objects that all have the same method (by name); it doesn’t matter if there are different types in the list.

Answered By: Martijn Pieters

newList = map(method, objectList) would call method(object) on each object in objectlist.

The way to do this with map would require a lambda function, e.g.:

map(lambda obj: obj.method(), objectlist)

A list comprehension might be marginally faster, seeing as you wouldn’t need a lambda, which has some overhead (discussed a bit here).

Answered By: Charles Marsh

If anyone was wondering, I did a small performance test (for my specific use case, so take it with a grain of salt):

import timeit
import numpy as np
from typing import Dict, NamedTuple

ROWS = 42
COLS = 12

class Item(NamedTuple):
    """An item"""

    row_min: int
    column_min: int
    row_max: int
    column_max: int

    def info(self) -> Dict[str, int]:
        """
        Converts item to dictionary
        """
        return {
            "row_min": self.row_min * ROWS,
            "column_min": self.row_max * ROWS,
            "row_max": self.column_min * COLUMNS,
            "column_max": self.column_max * COLUMNS,
        }

items = create_items()

def time_func(func):
    timings = timeit.repeat(func, number=10000, repeat=10)
    return np.mean(timings), np.std(timings)


tests = {
    "map w/lambda": lambda: map(lambda itm: itm.info(), items),
    "map w/static": lambda: map(Item.info, items),
    "map w/method caller": lambda: map(methodcaller('info'), items),
    "list comprehension": lambda: [itm.info() for itm in items],
}

for name, func in tests.items():
    mean, std = time_func(func)
    print(f"{name:25}: {mean:.5} +/- {std:.5}")

And I am getting these results:

map w/lambda             : 0.0026292 +/- 0.00063354
map w/static             : 0.0016314 +/- 8.6045e-05
map w/method caller      : 0.0021618 +/- 0.00019693
list comprehension       : 1.934 +/- 0.016754
Answered By: Tomer Cagan
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.