map(f, *iterables)
calls f
with elements from the iterables until one of them runs out of elements.
Except when there are zero iterables. That’s forbidden, for no apparent reason.
With the above general rule, map(f)
would give an iterator infinitely calling f
with zero arguments. Infinite because none of the zero iterables ever run out of elements.
So for example map(random)
would be an infinite iterator of random numbers, map(time)
would provide timestamps, and map(count)
would provide counters. These are cases I’ve actually wanted every now and then. I can use starmap(random, repeat(()))
instead, but all that extra code (also the import) is annoying.
Sadly that general rule is broken for the zero case, explicitly artificially banned by some “If map
is called with fewer than two arguments, raise an error” code. That’s the only thing standing in the way. If I simply change it to “fewer than one”, then map
behaves as desired. Doesn’t need extra code to support the behavior, the existing code naturally supports it already. And the only test that fails is one specifically testing the ban.
Why is it forbidden? I suspect it’s because that’s what Python 2 did, since map
built a list there and it doesn’t make sense to try to build an infinite list. But that reason doesn’t apply to Python 3.
The bans that stand in the way:
In map_new
:
if (numargs < 2) {
PyErr_SetString(PyExc_TypeError,
"map() must have at least two arguments.");
In map_vectorcall
:
if (nargs < 2) {
PyErr_SetString(PyExc_TypeError,
"map() must have at least two arguments.");
If I change them to 1
, then map
works as desired and the only test that fails is one specifically testing that this causes the error:
File "/workspaces/cpython/Lib/test/test_itertools.py", line 1372, in test_map
self.assertRaises(TypeError, map, operator.neg)
~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: TypeError not raised by map
(Wanted to request this for a while, now a side note in an issue by @takuom prompted me to pursue it.)