Skip to content

List[Union[...]] is not always satisfied by valid inputs. #3351

Closed

Description

@lovelydinosaur

When a list of unions is used, mypy will incorrectly error when a subset of the possible union types is used...

example.py:

from typing import List, Union

def mean(numbers: List[Union[int, float]]) -> float:
    return sum(numbers) / len(numbers)

some_numbers = [1, 2, 3, 4]
mean(some_numbers)

console:

$ mypy --version
mypy 0.470
$ mypy example.py 
example.py:7: error: Argument 1 to "mean" has incompatible type List[int]; expected List[Union[int, float]]

Presumably the inferred List[int] ought to be able to satisfy List[Union[int, float]], right?

Activity

  1. ilevkivskyi commented on May 10, 2017

    @ilevkivskyi
    Member

    Presumably the inferred List[int] ought to be able to satisfy List[Union[int, float]], right?

    No, List is invariant, and therefore List[int] is not a subtype (not compatible with) List[Union[int, float]] (the latter actually is just List[float], see PEP 484). The reason is that lists are mutable and therefore it is not safe to pass something of type List[int], where List[float] (or List[Union[int, float]]) is expected.

    See the docs for such situations. In your case I would recommend annotating your function as:

    def mean(numbers: Sequence[float]) -> float:

    (since you don't mutate numbers.)

  2. lovelydinosaur commented on May 10, 2017

    @lovelydinosaur
    Author

    Thanks for the clarification. 😄
    (& apologies for the noise!)

  3. gvanrossum commented on May 10, 2017

    @gvanrossum
    Member

    This should also be called out much earlier in the mypy docs. It's a very common "bug" report.

  4. ilevkivskyi commented on May 10, 2017

    @ilevkivskyi
    Member

    I think what Jukka proposed in #3352 (including link to docs in error messages) will solve the problem.

  5. ierezell commented on Sep 30, 2020

    @ierezell

    Sorry for the necroBump but I'm not able to have a list from Literals (as the exemple here) and I guess it's the same topic.

    I Feel that something is possible but I cannot make the typing system satisfied with that

    For exemple :

    MyListType = Literal['dogs', 'cats', 'crocodiles', 'ducks', 'bird', 'pony', 'fish'] # Tried with Union[Literal,Literal...] as well
    my_list : List[MyListType] = ['cats', 'crocodiles', 'ducks'] # Tried with Sequence as well and putting all elements in the MyListType
    

    I guess it's comming from Type "str" cannot be assigned to type "MyListType" which should...

  6. gvanrossum commented on Sep 30, 2020

    @gvanrossum
    Member

    @ierezell I don't get an error for that input. Please see Gitter for more help.

  7. Godsmith commented on Nov 3, 2021

    @Godsmith

    Presumably the inferred List[int] ought to be able to satisfy List[Union[int, float]], right?

    No, List is invariant, and therefore List[int] is not a subtype (not compatible with) List[Union[int, float]] (the latter actually is just List[float], see PEP 484). The reason is that lists are mutable and therefore it is not safe to pass something of type List[int], where List[float] (or List[Union[int, float]]) is expected.

    To explain why this is unsafe, to help people like me who have never heard the term invariant before, say that you have the following code:

    def add_float(numbers: List[Union[int, float]]):
        numbers.append(4.5)
    
    ints = [1, 2, 3]  # type: List[int]

    If you then do

    add_float(ints)

    you will end up with ints containing [1, 2, 3, 4.5] even though in the type definition it says that ints should only contain integers! And that is what they meant when they said passing the list into the method was "unsafe".

  8. added a commit that references this issue on Nov 1, 2025
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