Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Why does a dictionary union cause a type checking failure?#2104

Unanswered
dsotirho-ucsc asked this question inQ&A
Discussion options

https://mypy-play.net/?mypy=latest&python=3.12&gist=2c48503ebd2236fad252604292710529

Why does casec fail whilea andb are okay?

type AnyJSON = JSON | strtype JSON = dict[str, AnyJSON]a: JSON = {} | {}b: JSON = {'': ''}c: JSON = {'': ''} | {} # error
main.py:6: error: Incompatible types in assignment (expression has type "dict[str, str]", variable has type "JSON")  [assignment]main.py:6: note: "dict" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variancemain.py:6: note: Consider using "Mapping" instead, which is covariant in the value typeFound 1 error in 1 file (checked 1 source file)
You must be logged in to vote

Replies: 1 comment 1 reply

Comment options

Nice question :)

One needs to think here about assignability andwhen types are assigned / inferred, this involves which kind of python statements you are using.

Here are some more interesting points to look at:

b:JSON=reveal_type({'':''})

You might expect that the revealed type isdict[str, str], but it is (pyright/mypy):dict[str, dict[str, AnyJSON] | str], actually type-checkers see italready asJSON.

Now lets look at:

fromtypingimportcastb:JSON=cast("dict[str, str]", {'':''})# errord:dict[str,str]= {}b=d# error

I hope you can see why this fails. Because of invariance, you cannot assign adict[str, str] to a variable of typedict[str, AnyJSON] (i.e.,JSON). I am allowed to put anotherJSON intob, but ifb is d, I would but a dict into adict[str, str].

Lastly lets check out:

c:JSON= {'':''}|cast("JSON", {})# OK

Why is this fine and{'': ''} | {} not and what is the difference to the other cases.
The answer is you use an expression|, which is evaluated first andthen the result is tried to be assigned toJSON.
So if we look at the| or operator or rather howdict.__or__ is typed, the result of{'' | ''} | {} isdict[str, str] which is not assignable because of invariance.
As we involve aruntime expression for type-checkers this case is it like the error cases withb = d above. So in this case you need to either start with aJSON on the left or on the right fordict.__or__ to also produce aJSON in the outcome of the expression.

You must be logged in to vote
1 reply
@erictraut
Comment options

There are some additional subtleties here. To understand this behavior, you need to first understandbidirectional type inference.

Normally, a static type checker infers the type of expression{'': ''} asdict[str, str]. This type is not assignable toJSON due to invariance. However, with bidirectional type inference, a type checker can take into account additional "inference context", sometimes referred to as an "expected type". In the statementb: JSON = {'': ''}, the "expected type" isJSON, which influences the inference of{'': ''}.

Bidirectional type inference can be applied to a variety of expression forms including literal container expressions (lists, sets, dicts, tuples), as well as comprehensions, assignments, unary operators, lambdas, and call expressions. In general, it cannot be applied to binary operators like| because these do not map to simple calls. The| operator maps to some combination of calls to__or__ or__ror__ depending on the types of the two operands. This can get very complicated if the operand types are unions. For this reason, type checkers do not generally apply bidirectional type inference for binary operators (or augmented assignments or ternary operators) and instead evaluate the LHS and RHS operands without the benefit of an "expected type". The operator is then evaluated using the resulting types.

In your examplec: JSON = {'': ''} | {}, the LHS (without the benefit of bidirectional type inference) is evaluated asdict[str, str]. By default, pyright's default inference rules infer the RHS asdict[Unknown, Unknown], but in this case, pyright applies a limited form of bidirectional type inference and leverages the LHS type as the "expected type". It therefore evaluates the RHS type asdict[str, str]. It then evaluates the| operator, which in this case maps to the__or__ magic method and produces a result ofdict[str, str]. This isn't assignable toJSON, so a type violation is reported.

Extending your example,d: JSON = b | {} type checks without error. In this case, the normal type evaluation rules (without the benefit of bidirectional type inference) evaluate the type ofb | {} asdict[str, AnyJSON], which is assignable toJSON.

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Category
Q&A
Labels
None yet
3 participants
@dsotirho-ucsc@erictraut@Daraan

[8]ページ先頭

©2009-2025 Movatter.jp