Skip to content
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

"Unsupported operand types" error with dictionary that has some Decimal or Fraction values #4548

Closed
elias6 opened this issue Feb 7, 2018 · 7 comments

Comments

@elias6
Copy link
Contributor

elias6 commented Feb 7, 2018

I am using Python 3.6.4. I have a file with these 2 lines:

from decimal import Decimal
1 + {'a': 1, 'b': Decimal('1.5')}['a']

If I run it through mypy, I get this error:
supports_int_test.py:2: error: Unsupported operand types for + ("int" and "object")

This is strange. I would think that mypy should be able to figure out that regardless of which value is picked from the dictionary, it should be something that can be added to 1. The expression 1 + {'b': Decimal('1.5')}['b'] does not produce any errors.

If I use an int for the 'b' value (making all of the values ints), I don't get any errors. If I use a float for the 'b' value, I also don't get any errors. But if I use a Fraction, I get a similar error.

The expression 1 + {'b': Decimal('1.5')}['c'] (which deliberately tries to access a nonexistent key) doesn't give me any errors but 1 + {'a': 1, 'b': Decimal('1.5')}['c'] gives me a similar error.

This is happening with both mypy 0.560 and with the latest version from master (mypy 0.570-dev-26b51e554d7426a0aed6ad55bae7b10a4619c093).

@elias6
Copy link
Contributor Author

elias6 commented Feb 7, 2018

More about this bug:

  • If all of the values are the same type (ints, floats, Fractions, or Decimals), I get no errors.
  • If there are any strings or lists in the values, I get an error, but that should be expected because it is invalid to add an string or list to an int.
  • If there are some int values, some float values, and no other values, I get no errors.
  • I get errors in the following cases:
    • Some ints and some Decimals
    • Some floats and some Decimals
    • Some Fractions and some Decimals
    • Some ints and some Fractions
    • Some floats and some Fractions

@JelleZijlstra
Copy link
Member

This is because mypy tries to infer a common type for the dictionary, and because the only common type for int and Decimal that it knows about is object, it ends up inferring Dict[str, object]. This is a known and expected behavior, although I agree that it's not intuitive.

@elias6
Copy link
Contributor Author

elias6 commented Feb 7, 2018

This seems strange. How is this behavior expected? Who is it expected by?

As far as I know, all ints can be converted to Decimals, so shouldn't the type of the dictionary be Dict[str, Decimal] (analogously to how the type of a dictionary with both ints and floats as values is Dict[whatever, float])?

If not, then shouldn't there be some common type that would let a static type checker determine that it is valid to add any of the values to an int? Like maybe numbers.Number?

@ethanhs
Copy link
Collaborator

ethanhs commented Feb 7, 2018

I thought Decimal follows the SupportsInt protocol thus the value type should be SupportsInt*. Is this perhaps related to #2330?

@JukkaL
Copy link
Collaborator

JukkaL commented Feb 8, 2018

This seems strange. How is this behavior expected? Who is it expected by?

Mypy is working as designed, i.e. this is not a bug but a "feature". However, we may change this behavior in the future (#3816).

As far as I know, all ints can be converted to Decimals, so shouldn't the type of the dictionary be Dict[str, Decimal] (analogously to how the type of a dictionary with both ints and floats as values is Dict[whatever, float])?

Whether something can be converted to something else plays little role when inferring types in mypy. An int is not compatible with Decimal since it doesn't implement many of the methods supported by Decimal (and it also isn't a subclass of Decimal). int/float compatibility is a special case and is supported because int and float are very common and the incompatibilities between the types rarely cause problems.

If not, then shouldn't there be some common type that would let a static type checker determine that it is valid to add any of the values to an int? Like maybe numbers.Number?

There are multiple potential common types, and mypy can't make the decision reliably. Maybe somebody would expect numbers.Number, whereas somebody else would prefer Union[int, Decimal]. A third person might expect SupportsInt. I think that the most reasonable thing to do in cases like this is to require an explicit annotation (#3816).

@JukkaL
Copy link
Collaborator

JukkaL commented Feb 8, 2018

Closing as essentially a duplicate of #3816. Feel free to continue the discussion there.

@ilevkivskyi
Copy link
Member

Maybe somebody would expect numbers.Number, whereas somebody else would prefer Union[int, Decimal]. A third person might expect SupportsInt.

Finally, sometimes one can define a custom protocol, to list exactly what should be supported/provided by values in the Dict.

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

No branches or pull requests

5 participants