◐ Shell
clean mode source ↗

bpo-44019: Implement operator.call(). by anntzer · Pull Request #27888 · python/cpython

Having operator.call(obj, arg) mean type(obj).__call__(obj, arg) is
consistent with the other dunder operators. The semantics with *args, **kwargs then follow naturally from the single-arg semantics.

I realize that I proposed different semantics in the original bug report, but that didn't even actually match to the proposed use case; the second proposal in the thread is the correct one. The semantics here work for at least two different use cases that I have seen:

converters = [int, float, int, float]
lines = [  # Assume that we already `str.split` them.
    ["1", "2.2", "3", "4.4"],
    ["5", "6.6", "7", "8.8"],
]
[[*map(operator.call, converters, words)] for words in lines]
# faster than the list comprehension
[[conv(word) for conv, word in zip(converters, words)] for words in lines]

Here the performance actually matters (with numpy, one may easily be loading millions of rows). A simple benchmark gives

$ ./python -mtimeit -s 'from operator import call; funcs = [int, float, float, int]; inputs = ["1", "2", "3", "4"]' -- '[*map(call, funcs, inputs)]'
500000 loops, best of 5: 761 nsec per loop
$ ./python -mtimeit -s 'from operator import call; funcs = [int, float, float, int]; inputs = ["1", "2", "3", "4"]' -- '[func(inp) for func, inp in zip(funcs, inputs)]'
200000 loops, best of 5: 1.07 usec per loop

i.e. map(operator.call, ...) gives a significant speedup.