Skip to content

Expression does not infer float as result type #18133

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
mar10 opened this issue Nov 9, 2024 · 3 comments
Closed

Expression does not infer float as result type #18133

mar10 opened this issue Nov 9, 2024 · 3 comments
Labels
bug mypy got something wrong

Comments

@mar10
Copy link

mar10 commented Nov 9, 2024

Bug Report

The following return line reports an error although the expression returns the correct type.

To Reproduce

def demo_stddev(self) -> float:
    """Return the standard deviation of the time per iteration (aka SD, σ)."""
    values: list[float] = [1.0, 2.0, 3.0, 5.0]
    n = len(values)
    if n <= 1:
        return 0.0
    mean: float = sum(values) / n
    return (sum((x - mean) ** 2 for x in values) / n) ** 0.5

Expected Behavior

Emit no error

Actual Behavior

The following return line reports an error Returning Any from function declared to return "float"

Additional note

Appending # type: ignore is not a solution, because this will interfere with pyright:
Pyright correctly accepts the return type and will correctly report an error Unnecessary "# type: ignore" comment

Your Environment

  • VSCode
  • mypy 1.13.0 (compiled: yes)
  • Python 3.12.6
@hamdanal
Copy link
Collaborator

hamdanal commented Nov 9, 2024

You can set enableTypeIgnoreComments: false in your pyright configuration (see here) to tell pyright to leave type: ignore comments to mypy. You would still be able to ignore pyright specific type violations with pyright: ignore comments.

As for the error itself, I believe it is caused by the type declaration of float.__pow__ in typeshed. A negative float raised to the power of another float would result in a complex number, example:

>>> (-1.0)**0.5
(6.123233995736766e-17+1j)

There is no way for typeshed to tell if the result would be float or complex because there is no way to distinguish negative and positive floats so it was decided to annotate the method as returning Any. See the typeshed change here python/typeshed#6287.

@hamdanal hamdanal closed this as not planned Won't fix, can't repro, duplicate, stale Nov 9, 2024
@brianschubert
Copy link
Collaborator

To add, you can make this type check without a # type: ignore by using math.sqrt instead of ** 0.5:

return math.sqrt(sum((x - mean) ** 2 for x in values) / n)

That also has the benefit of being slightly faster:

$ python3.13 -m timeit -n100 'for x in range(100000): x**0.5'        
100 loops, best of 5: 3.34 msec per loop

$ python3.13 -m timeit -n100 -s 'import math' 'for x in range(100000): math.sqrt(x)'
100 loops, best of 5: 2.25 msec per loop

Alternatively, you could use cast(float, (...) ** 0.5), or disable the no-any-return error code with --no-warn-return-any to match pyright's behavior.

If your actual use case is computing the (population) standard deviation, you might also consider using statistics.pstdev.

@mar10
Copy link
Author

mar10 commented Nov 9, 2024

@hamdanal thank you for the explanation.
__pow__() indeed returns complex numbers for roots of negative values, so mypy was right.
It would have helped, if the method was annotated as returning float | complex, but that's not mypy's fault ;-)

I am aware of the global flags to disable checks.
As for the workarounds, I will go with math.sqrt(), thanks for the pointer, @brianschubert

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong
Projects
None yet
Development

No branches or pull requests

3 participants