Skip to content

Mandatory Iterator with "yield" #633

Closed

Description

@fcolas

Is it intended that the use of yield mandates the return type Iterator?
For example, if I want the squares of numbers:

def get_squares(n: int) -> Iterable[int]:
    return [i**2 for i in range(n)]

I cannot reimplement it as an iterator:

def get_squares(n: int) -> Iterable[int]:
    for i in range(n):
        yield i**2

without getting Iterator function return type expected for "yield" (even if Iterator actually is a subclass of Iterable).

However, it works with yield from:

def get_squares(n: int) -> Iterable[int]:
    yield from (i**2 for i in range(n))

What is the reasoning?

Activity

  1. rockneurotiko commented on Apr 13, 2015

    @rockneurotiko
    Contributor

    Hi!

    Like you say, 'yield from' check the return type and allow it if it's a subtype of 'Iterable' like you can see here:
    https://github.com/JukkaL/mypy/blob/master/mypy/checker.py#L1384

    The 'yield' word only allows 'Iterator' like you can see here:
    https://github.com/JukkaL/mypy/blob/master/mypy/checker.py#L1357

    Actually, yield and yield from, explained in the PEPs (225 and 380), return an Iterator (because both have a next function).

    So, I think that the change should be change 'yield from' to only allow an Iterator as return type more than let 'yield' allow Iterable subclass.

  2. fcolas commented on Apr 13, 2015

    @fcolas
    Author

    But the thing is that Iterator is already an Iterable:

    >>> import typing
    >>> issubclass(typing.Iterator, typing.Iterable)
    True
    

    In the same way that my first example returns a List which is a subtype of Iterable, I'd expect my function to be correctly typed if I advertise an Iterable implemented as an Iterator. What am I missing?

  3. rockneurotiko commented on Apr 13, 2015

    @rockneurotiko
    Contributor

    Yes, it's a subclass, and I've rediscovered why I wrote the 'yield from' support to allow Iterable subtype.

    >>> def a():
            yield 1
            yield 2
            yield 3
    
    >>> b = a()
    >>> dir(b)
    [..., '__iter__', ... '__next__',...]   # wrapped
    
    >>> def a():
             yield from [1,2,3]
    
    >>> b = a()
    >>> dir(b)
    [..., '__iter__', ...,  '__next__',...]  # wrapped
    

    Both, 'yield' and 'yield from' have next (iterator) and iter (iterable), so, changing whan I've said in my previous comment, seems correct to allow iterable subtypes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions