You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Currently the return type of A(...) is implicitly A even if A.__new__ return something else. If the return type is Any, maybe A(...) should have type Any, and if it's a subclass, we should also use that. Currently this is a little difficult to implement due to implementation limitations (we derive the type object identity from the return type of a type object). Maybe we should introduce a new attribute to Callable for this purpose.
Honestly I don't think that A() should ever return something that's not an instance of A (or a subclass), even though technically __new__() can return anything it likes. Somehow I worry that if __new__() were defined as returning Any for some class and from that we'd infer that A() returned Any we would weaken the type checking -- I'd much rather get an error if the declared return type of __new__() isn't A or a subclass of A.
Have you seen any real code that violates this rule?
No, I haven't seen real code that uses this, but I've seen tutorials that mention that this is possible -- they always mention that you probably shouldn't do that.
Okay, what about this:
If the return type is Any (either implicit or explicit), we don't check return statements within __new__ but the type of A() is still A.
If the return type of __new__ is not A or a subclass of A (and not Any), reject it as a type error.
If the return type B is a proper subclass of A, A() has type B.
@JukkaL On one hand, constructor returning an instance of different class is unacceptable in static typed languages. But on other hand, this feature is sometimes quite useful in Python. Even typing.py uses this for Union and for NamedTuple (since 3.6b1).
In general, it would be nice if mypy minimizes the number of false positives like flagging this code:
classC:
def__new__(cls):
return0C() +1
with Unsupported operand types for + ("C" and "int"). Maybe there could be an option to allow this.
We make use of __new__ to promote base classes to leaf classes in a few places. I don't love it, but it'll take some serious effort to untangle it, so I'm gonna +1 this for the case of base class promoting to subclasses.
To represent an unevaluated object like sin(x), the sin must be defined as a class rather than a function.
Nah, it could be a function returning a class instance. I trust there's a reason, but it's probably a little more complicated than that. :-)
But if you construct sin(0), it's automatically evaluated to a sympy Integer 0, which is not an instance of Application (the base class of sin that calls __new__).
To represent an unevaluated object like sin(x), the sin must be defined as a class rather than a function.
Nah, it could be a function returning a class instance. I trust there's a reason, but it's probably a little more complicated than that. :-)
If you use a function returning a class instance, you have to replicate the parameters of the constructor (if you want a proper signature / proper typing). I find this very inconvenient.
Honestly I don't think that A() should ever return something that's not an instance of A (or a subclass)
It depends on the purpose of Mypy:
Is it to enable people to do whateveer they want, in a type-safe way ?
Or is it to enforce good practices ?
I believe that the later would better be served by a linter than by a type-checker.
Reacted by Simon-Martin Schröder, Sergei Maertens, Paolo Bonzini, Felix Divo, Jean Jacques Warmerdam, Marti Raudsepp, William Bampton, Alex Lokhman, DougLeonard, Victorien and 17 more
SymPy.
To represent an unevaluated object like sin(x), the sin must be defined as a class rather than a function. But if you construct sin(0), it's automatically evaluated to a sympy Integer 0, which is not an instance of Application (the base class of sin that calls __new__).
I have been working adding type hints for this to sympy (sympy/sympy#25103) but hitting up against this issue. To be clear I think that SymPy's design here is not great and ideally expression heads would not be classes. Handling typing in a symbolic context is very difficult though and I'm still not sure what good ways to do this are (the Julia symbolics.jl folks are also struggling with the same thing). We are talking about it but changing this in sympy at this stage would be extremely difficult (there are hundreds of these classes spreading over ~100k lines of code and then more in downstream code as well).
I just want to note that pyright handles this differently and behaves exactly in the way that I would expect:
$ mypy q.pyq.py:8: error: Incompatible return type for "__new__" (returns "A", but must return a subtype of "B") [misc]q.py:11: note: Revealed type is "q.B"Found 1 error in 1 file (checked 1 source file)
$ pyright q.py... ./q.py:11:13 - information: Type of "B()" is "A"0 errors, 0 warnings, 1 information
I don't mind adding type: ignore for the "error" that mypy reports but there is no way to get mypy to understand that B() returns type A which ultimately means that mypy will reject valid downstream/user code that I don't control. In other words:
I don't necessarily object to mypy reporting the return hint as an error.
I think that mypy should still respect that hint for inference though (the hint is accurate regardless of whether anyone dislikes it).
It would be nice if mypy and pyright were consistent about inference especially when there are explicit hints.
Reacted by Simon-Martin Schröder, Clément Robert and Francisco Boni
I have a real-world case where __new__ can return a different type. I am working with algebraic expressions. These expressions are int-like and indeed can be evaluated to ints, but that evaluation can be expensive (large numbers involved), so we keep them unevaluated for as long as possible. Algebraic expression and actual int numbers are commingled and interact with each other.
When building an expression, I want to check for trivial values and abandon the expression if trivial values are found. This can be done in __new__. Here is some toy example code:
So for example, Add(1, 5) produces an Add object, but Add(0, 5) just produces the int5.
This runs just fine, and is perfectly sensible AFAICT. So to me it seems like Mypy's warning here is an opinionated complaint about style rather than an error being flagged. Maybe there could be a flag for allowing / forbidding this?
Note that the type of __new__ is explicitly declared, so there is no Any-related confusion.
Reacted by Simon-Martin Schröder, George Sakkis, Stanislav Zmiev and Paweł Rubin
My library, cached_classproperty uses this behavior to specify that a cached_classproperty returns a constant from its __new__ because unlike a regular cached_property, cached_classproperty will also get executed when accessed from a class.
Are we planning to do anything about this task? Yes, simplicity is important but people have given so many use cases here. Pyright supports this feature too.
Is it to enable people to do whateveer they want, in a type-safe way ?
Those two don't go along together. You either use static typing and follow the rules or make your code dynamic. You can't do "whatever you want" in a statically typed world. When you change the well defined signature, it's already unsafe because by nature people will make assumptions that your code follows the common practices/guidelines/rules. Your code lies about what it does and you want mypy to hide it.
Most of the examples can be replaced with a more appropriate - even if not the prettiest, but definitely less cursed - construct.
Reacted by Colby, Justin van der Krieken, Luca Dorigo and Randolf Scholz
When you change the well defined signature, it's already unsafe because by nature people will make assumptions that your code follows the common practices/guidelines/rules.
Can you point to anything that defines what the "rules" are for __new__?
The question as correctly asked above is whether mypy is supposed to be a type checker or a linter. Personally I would rather keep linting out of type checking and I definitely don't want opinionated lint rules to be baked into type inference.
Note that returning a different type here does not mean that the types are completely undefined. In the SymPy case there is a class Expr which has many subclasses like sin, Integer etc. The invariant is that if A is a subclass of Expr then A(...) will return an instance of Expr but not necessarily an instance of A. So sin(1) returns an instance of sin and sin(0) returns an instance of Integer, but always sin(anything) returns an instance of Expr because both sin and Integer (and hundreds of other classes) are subclasses of Expr.
It is not difficult to represent what __new__ does in type inference. It is just a class method that is called by type.__call__ which returns the same object:
Having looked a little through the mypy code that handles this I imagine that the mypy code could be both simpler and more robust if it could just represent type.__call__ in this way. Then mypy could apply ordinary type checking rules to __new__ as a class method rather than having many ad-hoc rules for __new__ and __init__ (and for metaclasses).
In fact if I just use a class method new instead of __new__ then mypy can understand what new returns just fine but still fails to understand __new__:
Here pyright understands the type of sin(0) but mypy gets it wrong:
$ mypy t.pyt.py:25: note: Revealed type is "t.Expr"t.py:26: note: Revealed type is "t.sin"Success: no issues found in 1 source file
$ pyright t.py./t.py:25:13 - information: Type of "sin.new(0)" is "Expr" ./t.py:26:13 - information: Type of "sin(0)" is "Expr"0 errors, 0 warnings, 2 informations ```
The fact that mypy can understand .new here but not .__new__ shows that this is because of ad-hoc special casing of __new__ rather than any limitation of mypy's general type inference capability. The fact that pyright can infer the type correctly in both cases shows that there is no reason why a type checker should not be able to do this.
The rejection of returning other types from cls.__new__ is just an arbitrary rule here:
We take the type from whichever of init and new is first in the MRO, preferring init if there is a tie.
bears no relation to the actual semantics of __init__ and __new__ as used in type.__call__ (there is no MRO combining these methods). The actual semantics are not complicated:
A.__new__ is called and returns an instance b of type B.
If b is an instance of A then b.__init__ (i.e. B.__init__) is called with the same arguments.
Note that A.__init__ may not be called at all depending on the type of what is returned by A.__new__ regardless of where in the MRO __new__ and __init__ are: __new__ always precedes __init__.
Reacted by Nick Drozd, Eric Traut, Simon-Martin Schröder, Colby and Justin van der Krieken
Can you point to anything that defines what the "rules" are for __new__?
I believe the returned object must be of the same type. It also doesn't seem to be a coroutine function.
I do see this
which implies it may return anything. Or it could imply that __init__ is being called only after successful __new__?
The invariant is that if A is a subclass of Expr then A(...) will return an instance of Expr but not necessarily an instance of A.
In my mind if you want to create an instance of A, you'd use A.__new__, if you want an instance of Expr, then you'd use Expr.__new__, it reads naturally, does it not? If we're in a situation where A.__new__ may not create an instance of A, I'd call such code unsafe because we end up with something unexpected. Maybe I fail to see something. What makes that code valid?
The question as correctly asked above is whether mypy is supposed to be a type checker or a linter
I see it as a correct type check, not as a lint. Since that method is tied to that class, then it should return instances of the type. However, if the docs are misleading and __new__ actually returns Any and what you found
this is because of ad-hoc special casing of __new__
is true, then you are correct. I wonder why mypy had that special case in the first place, has __new__ signature been changed?
I wonder why mypy had that special case in the first place, has __new__ signature been changed?
Nothing has changed except mypy making arbitrary decisions. The __new__ class method has been around a long time and one of the things that it has long been used for is to turn type.__call__ into a factory function that can return instances of other types. The fact that it can be used in this way is the reason that it is documented that __init__ will not be called if __new__ returns an instance of a different type (whose __init__ would likely have been called already but with different arguments).
The signature of __new__ has not changed because in fact nothing has changed about __new__ for a long time and its behaviour and usage predate all of typing and type hints in Python. Now that type signatures are a thing it has become possible to add type annotations for __new__ like:
classA:
def__new__(cls) ->B:
returnB()
However mypy has made two opinionated decisions about this that are not based on any previously documented rules or the actual behaviour of Python at runtime:
mypy reports an error for the A.__new__ method if the return type is not a subclass of A (mypy rejects valid code)
mypy assumes that A(...) will always return an instance of A in inference (mypy infers the wrong type).
I don't mind mypy reporting an error at __new__ if I can just add type: ignore there. The incorrect inference is the real problem here because it makes all the type checking wrong for any downstream users who are trying to use e.g. SymPy and mypy together and there is no way for me to add anything like type: ignore to make it work correctly.
The fact is that A(...) returns an instance of B as clearly stated in the type annotation for A.__new__. For mypy to infer anything else is just erroneous. Note that in my example code for Expr above mypy as a type checker accepts all of the code without any errors but then gets the inference for sin(0) wrong.
Reacted by Simon-Martin Schröder, Bill Huneke, Albin Skott, Paweł Rubin, Peter Gerlagh and San Kilkis
When you change the well defined signature, it's already unsafe because by nature people will make assumptions that your code follows the common practices/guidelines/rules.
A caller making incorrect assumptions about a return type is exactly the sort of thing that a typechecker ought to prevent, except that in this case the typechecker itself also incorrectly assumes the return type.
Here's an example:
from __future__ importannotationsfromrandomimportrandintfromtypingimportTYPE_CHECKINGclassA:
def__new__(cls) ->int|A: # type: ignore[misc]ifrandint(0, 1):
returnobject.__new__(cls)
else:
return5deff(x: A) ->None:
assertisinstance(x, A)
a=A()
ifTYPE_CHECKING:
reveal_type(a)
else:
print(a)
f(a)
This code will fail half the time. Mypy ought to be able to figure out why, but because it disregards the clearly specified signature of A.__new__, it can't see the error.
As @oscarbenjamin points out, this is a full-blown inference bug. Maybe the issue should be reopened?
Reacted by Simon-Martin Schröder, Albin Skott, Paweł Rubin, Colby, Hash, Justin van der Krieken, Luca Dorigo, Sveinung Gundersen, Roy Moore, plusls and 2 more
The discussion around whether mypy "should" type this seems to miss the point. There is plenty of well-formed Python out there in libraries that uses this pattern. If mypy cannot support it, it cannot produce accurate types for those files.
There is an open issue for __new__ returning unrelated classes at gh-15182. That issue mentions various libraries that need this and also references the typing spec which has been updated and now states:
For most classes, the return type for the __new__ method is typically Self, but other types are also allowed. For example, the __new__ method may return an instance of a subclass or an instance of some completely unrelated class.
Based on the discussion there I don't think that there is a disagreement about how mypy should handle this but rather someone needs to do the work to make it happen. My assessment having looked at the code is that it is not a trivial change to make though.
I think that future discussion on this point should go to gh-15182 rather than on this closed issue.
Reacted by Grant Mathews, Simon-Martin Schröder, Lukas Lang and Thibaut Decombe
Activity
JukkaL commentedon Nov 29, 2015
This is follow-up to #982.
gvanrossum commentedon Nov 29, 2015
Honestly I don't think that
A()should ever return something that's not an instance of A (or a subclass), even though technically__new__()can return anything it likes. Somehow I worry that if__new__()were defined as returning Any for some class and from that we'd infer thatA()returned Any we would weaken the type checking -- I'd much rather get an error if the declared return type of__new__()isn't A or a subclass of A.Have you seen any real code that violates this rule?
JukkaL commentedon Nov 30, 2015
No, I haven't seen real code that uses this, but I've seen tutorials that mention that this is possible -- they always mention that you probably shouldn't do that.
Okay, what about this:
Any(either implicit or explicit), we don't check return statements within__new__but the type ofA()is stillA.__new__is notAor a subclass ofA(and notAny), reject it as a type error.Bis a proper subclass ofA,A()has typeB.gvanrossum commentedon Nov 30, 2015
ilevkivskyi commentedon Oct 13, 2016
@JukkaL On one hand, constructor returning an instance of different class is unacceptable in static typed languages. But on other hand, this feature is sometimes quite useful in Python. Even
typing.pyuses this forUnionand forNamedTuple(since 3.6b1).In general, it would be nice if mypy minimizes the number of false positives like flagging this code:
with
Unsupported operand types for + ("C" and "int"). Maybe there could be an option to allow this.davidroeca commentedon Apr 26, 2017
One thought: the return type of
__new__, if explicitly defined and different from the class name itself, could implicitly be the class's supertype.This would also be a general alternative to python/typing#415 and could likely handle #3062
chadrik commentedon Dec 3, 2017
We make use of
__new__to promote base classes to leaf classes in a few places. I don't love it, but it'll take some serious effort to untangle it, so I'm gonna +1 this for the case of base class promoting to subclasses.Ours works something like this:
The revealed type is
Base, but I expect it toUnion[A, B].Also, this seems related to #3307.
nierob commentedon Apr 6, 2018
Another use case that I had is to have async constructor, poor man's __ainit__:
That actually fails in a different place, as in this case new returns the right thing, but
fails with:
11 remaining items
gvanrossum commentedon Mar 29, 2022
Nah, it could be a function returning a class instance. I trust there's a reason, but it's probably a little more complicated than that. :-)
Yeah, that's a good example actually.
moi90 commentedon Mar 31, 2022
If you use a function returning a class instance, you have to replicate the parameters of the constructor (if you want a proper signature / proper typing). I find this very inconvenient.
twoertwein commentedon Oct 1, 2022
Maybe an example where it might be reasonable for
__new__to not return the class instance.test.pyi:
The above would help to tightly type
pd.DataFrame.sdeframond commentedon Nov 17, 2022
It depends on the purpose of Mypy:
I believe that the later would better be served by a linter than by a type-checker.
oscarbenjamin commentedon May 4, 2023
I have been working adding type hints for this to sympy (sympy/sympy#25103) but hitting up against this issue. To be clear I think that SymPy's design here is not great and ideally expression heads would not be classes. Handling typing in a symbolic context is very difficult though and I'm still not sure what good ways to do this are (the Julia symbolics.jl folks are also struggling with the same thing). We are talking about it but changing this in sympy at this stage would be extremely difficult (there are hundreds of these classes spreading over ~100k lines of code and then more in downstream code as well).
I just want to note that pyright handles this differently and behaves exactly in the way that I would expect:
I don't mind adding
type: ignorefor the "error" that mypy reports but there is no way to get mypy to understand thatB()returns typeAwhich ultimately means that mypy will reject valid downstream/user code that I don't control. In other words:nickdrozd commentedon Sep 1, 2023
I have a real-world case where
__new__can return a different type. I am working with algebraic expressions. These expressions areint-like and indeed can be evaluated toints, but that evaluation can be expensive (large numbers involved), so we keep them unevaluated for as long as possible. Algebraic expression and actualintnumbers are commingled and interact with each other.When building an expression, I want to check for trivial values and abandon the expression if trivial values are found. This can be done in
__new__. Here is some toy example code:So for example,
Add(1, 5)produces anAddobject, butAdd(0, 5)just produces theint5.This runs just fine, and is perfectly sensible AFAICT. So to me it seems like Mypy's warning here is an opinionated complaint about style rather than an error being flagged. Maybe there could be a flag for allowing / forbidding this?
Note that the type of
__new__is explicitly declared, so there is noAny-related confusion.Believe arbitrary __new__ return types
zmievsa commentedon Sep 24, 2023
My library, cached_classproperty uses this behavior to specify that a cached_classproperty returns a constant from its
__new__because unlike a regularcached_property,cached_classpropertywill also get executed when accessed from a class.Are we planning to do anything about this task? Yes, simplicity is important but people have given so many use cases here. Pyright supports this feature too.
Booplicate commentedon Nov 24, 2023
Those two don't go along together. You either use static typing and follow the rules or make your code dynamic. You can't do "whatever you want" in a statically typed world. When you change the well defined signature, it's already unsafe because by nature people will make assumptions that your code follows the common practices/guidelines/rules. Your code lies about what it does and you want mypy to hide it.
Most of the examples can be replaced with a more appropriate - even if not the prettiest, but definitely less cursed - construct.
oscarbenjamin commentedon Nov 24, 2023
Can you point to anything that defines what the "rules" are for
__new__?The docs are clear that
cls.__new__may return an object that is not an instance ofcls:https://docs.python.org/3/reference/datamodel.html#object.__new__
The question as correctly asked above is whether mypy is supposed to be a type checker or a linter. Personally I would rather keep linting out of type checking and I definitely don't want opinionated lint rules to be baked into type inference.
Note that returning a different type here does not mean that the types are completely undefined. In the SymPy case there is a class
Exprwhich has many subclasses likesin,Integeretc. The invariant is that ifAis a subclass ofExprthenA(...)will return an instance ofExprbut not necessarily an instance ofA. Sosin(1)returns an instance ofsinandsin(0)returns an instance ofInteger, but alwayssin(anything)returns an instance ofExprbecause bothsinandInteger(and hundreds of other classes) are subclasses ofExpr.It is not difficult to represent what
__new__does in type inference. It is just a class method that is called bytype.__call__which returns the same object:Having looked a little through the mypy code that handles this I imagine that the mypy code could be both simpler and more robust if it could just represent
type.__call__in this way. Then mypy could apply ordinary type checking rules to__new__as a class method rather than having many ad-hoc rules for__new__and__init__(and for metaclasses).In fact if I just use a class method
newinstead of__new__then mypy can understand whatnewreturns just fine but still fails to understand__new__:Here pyright understands the type of
sin(0)but mypy gets it wrong:The fact that mypy can understand
.newhere but not.__new__shows that this is because of ad-hoc special casing of__new__rather than any limitation of mypy's general type inference capability. The fact that pyright can infer the type correctly in both cases shows that there is no reason why a type checker should not be able to do this.The rejection of returning other types from
cls.__new__is just an arbitrary rule here:mypy/mypy/checker.py
Lines 1535 to 1543 in 5b1a231
The inference in mypy bakes in the assumption that
cls.__new__returns an instance ofclse.g.:mypy/mypy/checkmember.py
Lines 1244 to 1312 in 5b1a231
The comment in the code
bears no relation to the actual semantics of
__init__and__new__as used intype.__call__(there is no MRO combining these methods). The actual semantics are not complicated:A.__new__is called and returns an instancebof typeB.bis an instance ofAthenb.__init__(i.e.B.__init__) is called with the same arguments.Note that
A.__init__may not be called at all depending on the type of what is returned byA.__new__regardless of where in the MRO__new__and__init__are:__new__always precedes__init__.Booplicate commentedon Nov 24, 2023
I believe the returned object must be of the same type. It also doesn't seem to be a coroutine function.
I do see this

which implies it may return anything. Or it could imply that
__init__is being called only after successful__new__?In my mind if you want to create an instance of
A, you'd useA.__new__, if you want an instance ofExpr, then you'd useExpr.__new__, it reads naturally, does it not? If we're in a situation whereA.__new__may not create an instance ofA, I'd call such code unsafe because we end up with something unexpected. Maybe I fail to see something. What makes that code valid?I see it as a correct type check, not as a lint. Since that method is tied to that class, then it should return instances of the type. However, if the docs are misleading and
__new__actually returnsAnyand what you foundis true, then you are correct. I wonder why mypy had that special case in the first place, has
__new__signature been changed?oscarbenjamin commentedon Nov 24, 2023
Nothing has changed except mypy making arbitrary decisions. The
__new__class method has been around a long time and one of the things that it has long been used for is to turntype.__call__into a factory function that can return instances of other types. The fact that it can be used in this way is the reason that it is documented that__init__will not be called if__new__returns an instance of a different type (whose__init__would likely have been called already but with different arguments).The signature of
__new__has not changed because in fact nothing has changed about__new__for a long time and its behaviour and usage predate all of typing and type hints in Python. Now that type signatures are a thing it has become possible to add type annotations for__new__like:However mypy has made two opinionated decisions about this that are not based on any previously documented rules or the actual behaviour of Python at runtime:
A.__new__method if the return type is not a subclass ofA(mypy rejects valid code)A(...)will always return an instance ofAin inference (mypy infers the wrong type).I don't mind mypy reporting an error at
__new__if I can just addtype: ignorethere. The incorrect inference is the real problem here because it makes all the type checking wrong for any downstream users who are trying to use e.g. SymPy and mypy together and there is no way for me to add anything liketype: ignoreto make it work correctly.The fact is that
A(...)returns an instance ofBas clearly stated in the type annotation forA.__new__. For mypy to infer anything else is just erroneous. Note that in my example code forExprabove mypy as a type checker accepts all of the code without any errors but then gets the inference forsin(0)wrong.nickdrozd commentedon Nov 29, 2023
A caller making incorrect assumptions about a return type is exactly the sort of thing that a typechecker ought to prevent, except that in this case the typechecker itself also incorrectly assumes the return type.
Here's an example:
This code will fail half the time. Mypy ought to be able to figure out why, but because it disregards the clearly specified signature of
A.__new__, it can't see the error.As @oscarbenjamin points out, this is a full-blown inference bug. Maybe the issue should be reopened?
johnfn commentedon Feb 28, 2025
The discussion around whether mypy "should" type this seems to miss the point. There is plenty of well-formed Python out there in libraries that uses this pattern. If mypy cannot support it, it cannot produce accurate types for those files.
oscarbenjamin commentedon Feb 28, 2025
There is an open issue for
__new__returning unrelated classes at gh-15182. That issue mentions various libraries that need this and also references the typing spec which has been updated and now states:Based on the discussion there I don't think that there is a disagreement about how mypy should handle this but rather someone needs to do the work to make it happen. My assessment having looked at the code is that it is not a trivial change to make though.
I think that future discussion on this point should go to gh-15182 rather than on this closed issue.