I raised a related problem a while back when I found that random.sample can
only take a sequence. The example I gave was randomly sampling points on a
2D grid to initialize a board for Conway's Game of Life:

>>> def random_board(height: int, width: int, ratio: float = 0.5) ->
Set[Tuple[int, int]]:
...     """ produce a set of points randomly chosen from an height x width
grid """
...     all_points = itertools.product(range(height), range(width))
...     num_samples = ratio*height*width
...     return set(random.sample(all_points, num_samples))
...
>>> random_board(height=5, width=10, ratio=0.25)
TypeError: Population must be a sequence or set.  For dicts, use list(d).

It seems like there should be some way to pass along the information that
the size *is* known, but I couldn't think of any way of passing that info
along without adding massive amounts of complexity everywhere.

If map is able to support len() under certain circumstances, it makes sense
that other iterators and generators would be able to do the same. You might
even want a way to annotate a generator function with logic about how it
might support len().

I don't have an answer to this problem, but I hope this provides some sense
of the scope of what you're asking.

On Mon, Nov 26, 2018 at 3:36 PM Kale Kundert <k...@thekunderts.net> wrote:

> I just ran into the following behavior, and found it surprising:
>
> >>> len(map(float, [1,2,3]))
> TypeError: object of type 'map' has no len()
>
> I understand that map() could be given an infinite sequence and therefore
> might not always have a length.  But in this case, it seems like map()
> should've known that its length was 3.  I also understand that I can just
> call list() on the whole thing and get a list, but the nice thing about
> map() is that it doesn't copy data, so it's unfortunate to lose that
> advantage for no particular reason.
>
> My proposal is to delegate map.__len__() to the underlying iterable.
> Similarly, map.__getitem__() could be implemented if the underlying
> iterable supports item access:
>
> class map:
>
>     def __init__(self, func, iterable):
>         self.func = func
>         self.iterable = iterable
>
>     def __iter__(self):
>         yield from (self.func(x) for x in self.iterable)
>
>     def __len__(self):
>         return len(self.iterable)
>
>     def __getitem__(self, key):
>         return self.func(self.iterable[key])
>
> Let me know if there any downsides to this that I'm not seeing.  From my
> perspective, it seems like there would be only a number of (small)
> advantages:
>
> - Less surprising
> - Avoid some unnecessary copies
> - Backwards compatible
>
> -Kale
> _______________________________________________
> Python-ideas mailing list
> Python-ideas@python.org
> https://mail.python.org/mailman/listinfo/python-ideas
> Code of Conduct: http://python.org/psf/codeofconduct/
>
_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to