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

Annotating custom constructor methods for generic types.#2025

Answeredbyerictraut
randolf-scholz asked this question inQ&A
Discussion options

How to annotateFoo.new in the example below so that it behaves as expected? Code sample inpyright playground

# fmt: offfromcollections.abcimportMappingfromtypingimportoverloaddict(bar=3)# SHOULD PASS  (pyright: ✅, mypy: ✅)dict[str,int](bar=3)# SHOULD PASS  (pyright: ✅, mypy: ✅)dict[int,int](bar=3)# SHOULD ERROR (pyright: ✅, mypy: ❌)classFoo[K,V](Mapping[K,V]):@overload@classmethoddefnew(cls,items:Mapping[K,V],/)->"Foo[K, V]": ...@overload@classmethoddefnew(cls,items:Mapping[str,V]= ...,/,**kwargs:V)->"Foo[str, V]": ...Foo.new(bar=3)# SHOULD PASS  (pyright: ✅, mypy: ✅)Foo[str,int].new(bar=3)# SHOULD PASS  (pyright: ✅, mypy: ✅)Foo[int,int].new(bar=3)# SHOULD ERROR (pyright: ❌, mypy: ❌)

My analysis:mypy doesn't error ondict[int, int](bar=3). I think this is a bug.pyright does error, but it's based on a__init__ overload, where we can constrainself. This is not available for a custom constructor. I tried to no avail:

  1. constrainingcls: "Foo[str, V]" similarly innew, but it raises new errors. Code sample inpyright playground
  2. use polymorphic overload, but it doesn't change the inference. Code sample inpyright playground
You must be logged in to vote

Any idea why it fails to bindV=int (or possiblyV=Literal[3])?

Yes,Foo.new bindsFoo[Any, Any] to thenew method. The types of class-scoped type variables are inferred from argument types only when you call a constructor. This doesn't work for other methods. In other cases, the specialization comes from the bound class — in this case,Foo orFoo[Any, Any] since you haven't specified any default values for the type variables.

Replies: 1 comment 5 replies

Comment options

I think option 1 is the right approach, but you need to usetype[Foo[str, V]] for thecls parameter annotation. If I make that change, it seems to work fine in pyright.

You must be logged in to vote
5 replies
@randolf-scholz
Comment options

Like so? Code sample inpyright playground

This does seem to work withpyright,mypy seems to bug out on any new call withK≠str. Also:

_m0=Foo.new(bar=3)reveal_type(_m0)# "Foo[str, Unknown]"

Any idea why it fails to bindV=int (or possiblyV=Literal[3])?

@erictraut
Comment options

Any idea why it fails to bindV=int (or possiblyV=Literal[3])?

Yes,Foo.new bindsFoo[Any, Any] to thenew method. The types of class-scoped type variables are inferred from argument types only when you call a constructor. This doesn't work for other methods. In other cases, the specialization comes from the bound class — in this case,Foo orFoo[Any, Any] since you haven't specified any default values for the type variables.

Answer selected byrandolf-scholz
@randolf-scholz
Comment options

Just to make sure that I get this right, when you say:

The types of class-scoped type variables are inferred from argument types only when you call*a constructor.* This doesn't work for other methods.

What exactly counts as*a constructor*? Only the standard constructormetaclass.__call__/__new__/__init__?

Well, actually, ... it does seem to produce the desired inference if we definenew inside a custom metaclass. 🤓 Code sample inpyright playground

I guess this makes sense insofar as that if we were to access, say, some generic class-attributes inside.new, it wouldn't be clear what their types are inside the constructor itself.

So if we want full type-checking for a custom constructor, one either should use a function defined outside the class, a@staticmethod or a custom metaclass (if we need access to certain class-variables at construction time).

Learned something new today, thanks as always for your insights.

@randolf-scholz
Comment options

Though I have to say, having to define metaclasses makes this whole thing quite un-ergonomic. In fact, there seems to be another type-checker divergence here:

Code sample inpyright playground,https://mypy-play.net/?mypy=latest&python=3.13&gist=d902e42012932c4a8892c75c46d170d4

fromtypingimportSelfclassVector[T]:@classmethoddeffrom_list(cls,x:list[T])->Self: ...# shows mypy: Vector[str], pyright: Vector[Unknown]reveal_type(Vector.from_list(["abc"]))
@randolf-scholz
Comment options

Would you considermypy's inference in the example above a bug then?

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

[8]ページ先頭

©2009-2025 Movatter.jp