Skip to content

Improve unification of conditional expressions #1094

Closed

Description

@ZeeD

I have this snippet of code

from typing import Any,List

class Node:
    def __init__(self, value:Any, *, children:List['Node']=None) -> None:
        self.value = value
        self.children = [] if children is None else children # type: List[Node]

if I use mypy I get

Tree.py: note: In member "__init__" of class "Node":
Tree.py:10: error: Incompatible types in assignment (expression has type "object", variable has type List[Node])

I think it's something related to the children parameter setted to None, but the problem is... how do I specify that yes, maybe the children parameter can be None, but self.children, by construction is always a List of Nodes?

Activity

  1. gvanrossum commented on Dec 22, 2015

    @gvanrossum
    Member
  2. JukkaL commented on Dec 28, 2015

    @JukkaL
    Collaborator

    The if..else expression doesn't propagate the type context (in your example List[Node]) to the child expressions. Propagating the context would let mypy infer type List[Node] for the left operand.

    A related way to fix this would be to use the type of the right operand as the type context for the first expression, in case the latter has an incomplete inferred type. This would have the benefit of not requiring a type annotation.

    I think that you could also work around the problem by rearranging the expression:

    self.children = children if children is not None else []
    
  3. gvanrossum commented on Dec 30, 2015

    @gvanrossum
    Member
  4. added this to the milestone on Mar 2, 2016
  5. gvanrossum commented on Mar 8, 2016

    @gvanrossum
    Member

    I keep running into this. The type of (a if x else b) where a and b don't have the same type often degenerates to object or some other useless type rather than the intersection of the two types. Latest example:

    class A: pass
    class B:
        def foo(self): pass
    class C(A, B): pass  # No error if we switch this to C(B, A)
    a = B() if True else C()
    a.foo()  # E: "object" has no attribute "foo"
    

    Also, e.g.in Python 2 mode:

    float(Decimal(0) if True else 0)  # E: No overload variant of "float" matches argument types [builtins.object]
    

    In Python 3 mode this gives

    error: Argument 1 to "float" has incompatible type "object"; expected "Union[SupportsFloat, str, bytes]"
    
  6. changed the title [-]Incompatible types in assignment (handling optional parameters)[/-] [+]Improve unification of conditional expressions[/+] on Apr 14, 2016
  7. self-assigned this
    on Apr 15, 2016
  8. gvanrossum commented on Apr 15, 2016

    @gvanrossum
    Member

    I'm going to have a crack at this.

  9. added 2 commits that reference this issue on Apr 18, 2016
    1d2a30d
    1061937
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugmypy got something wrong

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions