Skip to content

Type of variable after isinstance check with 'and' #473

Closed

Description

@spkersten

After a guard with isinstance, mypy should consider the variable to be of that type.

For example:

def f(x: A) -> None:
    if isinstance(x, B) and x.value:
        pass

class A:
    pass
class B(A):
    value = 0

Here, mypy reports that x doesn't have a field value. This can of course be solved with a cast, but that is annoying to type and clutters the code. At least in simple case, mypy could infer that x is of type B in the rhs of the and-statement and in the block following the if-statement.

Activity

  1. JukkaL commented on Oct 12, 2014

    @JukkaL
    Collaborator

    This already works if the condition only has an isinstance check:

    def f(x: A) -> None:
        if isinstance(x, B):
            print(x.value)  # Ok
    
    class A:
        pass
    class B(A):
        value = 0

    Mypy should support more cases, including and expressions (your example should be valid).

    Here is another case that could be useful (this could be a separate issue):

    def f(x: object) -> None:
        if isinstance(x, int) or isinstance(x, str):   # or isinstance(x, (int, str))
            # type of x should be Union[int, str]
            pass

    These improvements are important for proper type checking of None values (currently None is treated pretty much like null in Java, i.e. it is implicitly included in every type). For example, consider this code:

    def f(x: Union[int, None]) -> None:
        if x and x > 3:   # Ok
            print(x * 2)   # Ok
        print(x * 2)   # Error, since can't multiply None

    Currently there is no error reported for the the second print statement.

  2. JukkaL commented on Oct 12, 2014

    @JukkaL
    Collaborator

    Added related issues #475 and #476.

  3. refi64 commented on Oct 12, 2014

    @refi64
    Contributor

    Just to put it out there, the Cobra approach (see Compile-time Nil Tracking) to null values seems nicer, in my opinion, than using Union[T, None]. Basically, it would mean something like this:

    def x(a: Nullable[int]) -> int:
        print(a*a) # ok; a is still an int
        return a # error; cannot convert Nullable type to none-Nullable type
        if a: return a # ok; a cannot be None
  4. gvanrossum commented on Oct 12, 2014

    @gvanrossum
    Member

    My preferred spelling would be Optional[int]. (Nullable reeks of SQL. :-)

    On Sun, Oct 12, 2014 at 1:57 PM, Ryan Gonzalez [email protected]
    wrote:

    Just to put it out there, the Cobra
    http://cobra-language.com/docs/quality/ approach (see Compile-time Nil
    Tracking) to null values seems nicer, in my opinion, than using Union[T,
    None]. Basically, it would mean something like this:

    def x(a: Nullable[int]) -> int:
    print(a*a) # ok; a is still an int
    return a # error; cannot convert Nullable type to none-Nullable type
    if a: return a # ok; a cannot be None

    —
    Reply to this email directly or view it on GitHub
    #473 (comment).

    --Guido van Rossum (python.org/~guido)

  5. refi64 commented on Oct 12, 2014

    @refi64
    Contributor

    @gvanrossum You're very right; I like Optional better.

  6. JukkaL commented on Oct 12, 2014

    @JukkaL
    Collaborator

    @kirbyfan64 I agree, special syntax would be preferable. But I'm not sure why a*a should be okay if a has an optional type. None * None raises an exception.

  7. JukkaL commented on Oct 12, 2014

    @JukkaL
    Collaborator

    Added issue #477 for Optional[T].

  8. changed the title [-]Type of variable after isinstance check[/-] [+]Type of variable after isinstance check with 'and'[/+] on Oct 13, 2014
  9. rwbarton commented on Jun 15, 2016

    @rwbarton
    Contributor

    The original program here has worked since commit 0b151c6. Also, Jukka's "another case" will be addressed by #1712, and the example with Union[int, None] already works under strict optional checking. So closing this as fixed.

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions