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
float**float is currently defined as returning float, but in Python 3, (negative float) ** (non-whole float) returns a complex number. (In Python 2 it raises ValueError.)
I'm not sure what to do about this -- in mypy a plugin won't be very effective, because typically the sign of the base (left operand) is not known statically (as opposed to the exponent, which is often a literal, which is how we addressed this for int**(negative int)).
Declaring the result a complex is technically correct (since float is effectively a subclass of complex, just like int is effectively a subclass of float), but probably going to be a pain. Making it Union[float, complex] is likely worse. Keeping it float is a lie and may lead to unanticipated errors, and making it Any (like we do for int**int when we don't know the sign of the exponent) a cop-out that just hides more errors.
All in all I think we should change this to complex, even if it's a pain. Any other opinions?
Making it Any or complex seem like the most reasonable options. For consistency with how int**int and open are annotated (they have Anys in their return types) we could make the return type Any.
I would also slightly prefer returning complex instead of Any. This is mostly going to be used in mathematical applications that can be expected to handle this case. If it turns out to be a huge pain in practice, we can always change to Any later.
PR #2662, which implements this, causes CI to fail, due to problems with the sample code copied from the standard lib's random module. A fix for this is in python/mypy#5988. This is a good example of real world code that this would affect. I am still in favor of using a complex return type instead of Any, but I do not work with mathematical code a lot.
Returning complex instead of float from __pow__() makes some implementations fail that assumed it to return float. We need to discuss whether that is acceptable or we need a different solution.
I would be unhappy if we changed this to -> complex. I think it's a design mistake that we made this work in Python 3 in the first place -- if you look at the math and cmath modules, the guiding principle there is that the functions in math never return complex results, only the ones in cmath do. The equivalent rule for ** would have been to preserve the Python 2 behavior, where (-1)**0.5 raises ValueError, and if you want a complex result you have to use e.g. complex(-1)**0.5. (Concrete example: math.sqrt(-1) raises ValueError, while cmath.sqrt(-1) returns 1j.)
For the similar case of int/int, when we made this return float int Python 3, we did it differently: it always returns float, even if the result is mathematically an integer.
I expect it would be hugely inconvenient if e.g. x**0.5 were to be inferred as a complex value -- it would encourage people to add useless asserts or # type: ignore comments or even casts just to make mypy understand that the expected result is a float (because x is known or assumed to be nonnegative).
As far as I understand, in #2662, I think there should only be an overload for when x is an int, rest should be as it was before. I hope I'm not missing anything here?
I am a bit conflicted on this issue. On the one hand, returning float - while probably correct in the majority of cases - can hide bugs (but so can returning Any). On the other hand, as Guido said, returning complex is a big pain, as python/mypy#5988 shows. As someone who doesn't write much mathematical code, I'll defer to Guido's judgement.
So basically, revert the changes the changes, no overload, just return float as before. But it would be helpful to add a comment to __pow__() that it returns complex if self is negative and x is not whole.
Activity
JukkaL commentedon Jun 14, 2017
Making it
Anyorcomplexseem like the most reasonable options. For consistency with how int**int andopenare annotated (they haveAnys in their return types) we could make the return typeAny.Return Any from float.__(r)pow__
srittau commentedon Sep 11, 2018
I would also slightly prefer returning
complexinstead ofAny. This is mostly going to be used in mathematical applications that can be expected to handle this case. If it turns out to be a huge pain in practice, we can always change toAnylater.builtins.pyi: Return complex from __pow__() and
builtins.pyi: Return complex from __(r)pow__()
builtins.pyi: Return complex from __(r)pow__()
srittau commentedon Dec 2, 2018
PR #2662, which implements this, causes CI to fail, due to problems with the sample code copied from the standard lib's
randommodule. A fix for this is in python/mypy#5988. This is a good example of real world code that this would affect. I am still in favor of using acomplexreturn type instead ofAny, but I do not work with mathematical code a lot.utkarsh2102 commentedon Dec 2, 2018
I still did not understand the reason for CI failure :/
srittau commentedon Dec 2, 2018
Returning
complexinstead offloatfrom__pow__()makes some implementations fail that assumed it to returnfloat. We need to discuss whether that is acceptable or we need a different solution.utkarsh2102 commentedon Dec 2, 2018
Ah, okay.
gvanrossum commentedon Dec 3, 2018
I would be unhappy if we changed this to
-> complex. I think it's a design mistake that we made this work in Python 3 in the first place -- if you look at themathandcmathmodules, the guiding principle there is that the functions inmathnever return complex results, only the ones incmathdo. The equivalent rule for**would have been to preserve the Python 2 behavior, where(-1)**0.5raises ValueError, and if you want a complex result you have to use e.g.complex(-1)**0.5. (Concrete example:math.sqrt(-1)raises ValueError, whilecmath.sqrt(-1)returns1j.)For the similar case of
int/int, when we made this return float int Python 3, we did it differently: it always returns float, even if the result is mathematically an integer.I expect it would be hugely inconvenient if e.g.
x**0.5were to be inferred as a complex value -- it would encourage people to add useless asserts or# type: ignorecomments or even casts just to make mypy understand that the expected result is a float (becausexis known or assumed to be nonnegative).utkarsh2102 commentedon Dec 3, 2018
This makes sense.
@srittau, should I change
complextoAnythen?gvanrossum commentedon Dec 3, 2018
Why not leave well enough alone? If someone uses
a**bwith negativeaand expects a complex, let them rewrite it ascomplex(a)**b.utkarsh2102 commentedon Dec 3, 2018
As far as I understand, in #2662, I think there should only be an overload for when
xis anint, rest should be as it was before. I hope I'm not missing anything here?srittau commentedon Dec 3, 2018
I am a bit conflicted on this issue. On the one hand, returning
float- while probably correct in the majority of cases - can hide bugs (but so can returningAny). On the other hand, as Guido said, returningcomplexis a big pain, as python/mypy#5988 shows. As someone who doesn't write much mathematical code, I'll defer to Guido's judgement.So basically, revert the changes the changes, no overload, just return
floatas before. But it would be helpful to add a comment to__pow__()that it returnscomplexif self is negative andxis not whole.srittau commentedon Dec 3, 2018
Also, in my ongoing effort to promote
AnyOf(python/typing#566), this would be a good use case. Same for range types (python/typing#554).utkarsh2102 commentedon Dec 3, 2018
Sure, on it.
builtins.pyi: Return float from __(r)pow__()
builtins.pyi: Return complex from __(r)pow__() (#2662)
builtins.pyi: Return complex from __(r)pow__() (python#2662)