Repository navigation
Improve duck type compatibility of int with float #100268
Description
Activity
- addedtype-featureA feature request or enhancementA feature request or enhancement
on Dec 15, 2022 Issues like this need to be filed over at https://github.com/python/typing rather than on this issue tracker. Perhaps @JelleZijlstra or @gvanrossum can transfer it over.
Having said that, while I agree in principle with many of your points, I doubt we'll be changing this. It's been this way for many years now, and changing it now would be very breaking for a large number of users.
@AlexWaygood Thank you and sorry for the incorrect filing of this issue.
I agree that this has been part of Python since the beginning of support of typing. It would be a big BC break (affecting also all my projects).
I was thinking about sending a PR to mypy that would "fix" this using an optional switch. But then I realised that because of how the PEP484 is phrased, it would be incorrect to disallow assigning int to float. Maybe there's a way it could be rephrased so that it would allow for such option in Mypy? In current state, my PR would be almost certainly rejected.
- changed the title
[-]Arguments annotated with `float` should not allow value of type `int.`[/-][+]Arguments annotated with `float` should not allow value of type `int`[/+]on Dec 15, 2022 While your premise it true, a float is not an int. I don't think this has a chance. The PEP 484 decision was intentional and pragmatic. It reflects what is done in a lot of real world code (functions that accept floats also accept ints). And subsequent to the PEP being approved, the decision proved to be useful in practice.
To change the decision would require broad discussion and buy in and perhaps another PEP. If you want to pursue that route, I recommend starting a discussion on the forums. Just expect that it will be an uphill battle. The original decisions wasn't made lightly.
Reacted by Alex Waygood and Carl MeyerI agree with what was said above, but a few more thoughts:
- mypy could add an option to disable the int/float (and complex/float) promotion, but one complication would be that typeshed stubs are written under the assumption that float includes int. So it would have to continue to apply the promotion when checking calls to stdlib functions.
- You raise a good point about methods that exist on
floatbut notint. Let's see if we can resolve those differences:int.is_integercould be added with the trivial behavior of always returning True. This seems harmless to me but others may have objections; feel free to open an issue requesting it be added. A similar precedent iscomplex.__complex__, which was added in Should we define complex.__complex__ and bytes.__bytes__? #68422.float.fromhexis a classmethod. If we addedint.fromhexand made it accept the same format asfloat.fromhex, it would have to return a float, which would be weird for an int classmethod; and if it accepted a different format, we wouldn't actually help int/float interoperability.float.hexreturns a hex representation that is specific to floats, and it would be weird to have this method on ints but return a float-specific representation.
Reacted by Alex Waygood, Carl Meyer and Gregory P. Smithint.is_integercould be added with the trivial behavior of always returning True. This seems harmless to me but others may have objections; feel free to open an issue requesting it be added. A similar precedent iscomplex.__complex__, which was added in Should we define complex.complex and bytes.bytes? #68422.
I'd be happy to see this added, and I agree it would lessen some of the pain here. Another precedent is the way that
intobjects have fairly pointlessimagandrealproperties. I believe this is purely so they conform a little more closely with the numeric tower in thenumbersmodule, which specifies thatIntegralnumbers are a subtype ofComplexnumbers:>>> x = 5 >>> x.real 5 >>> x.imag 0
Int is supposed to be duck type compatible with float. So yes, it should have all (instance) methods of float.
Reacted by Alex Waygood, Carl Meyer, Jakub Tesárek and Gregory P. SmithReacted by Gary YendellYes,
fromhex()is an alternative constructor and a classmethod, so is much less relevant to LSP concerns.- changed the title
[-]Arguments annotated with `float` should not allow value of type `int`[/-][+]Improve duck type compatibility of int with float[/+]on Dec 22, 2022 I think there's consensus that
is_integershould be added toint. This is safe, straightforward and has precedent with the complex attributes, like Alex mentions. I agree that we should leavehexalone.This is brought up somewhat regularly on mypy's issue tracker. I think there's a little bit of an XY complaint happening here in that the actual behaviour that static typing users get confused by is
isinstance(1, float) is False— and static typing users maybe don't really care about theis_integermethod. Note that mypy's behaviour has recently started to reflect the runtime more accurately (see python/mypy#13781 ), which should help with these complaints.OP also provides a
split_whole_and_decimalexample, but I don't find that compelling sincefloat.__str__will happily spit out1e+20Reacted by Alex Waygood, Jelle Zijlstra, Carl Meyer and Dave- added a commit that references this issue
on Dec 22, 2022 @hauntsaninja Do you have bandwidth to make a corresponding PR for adding
is_integertofractions.Fraction? (decimal.Decimalis more involved, and might be better left to another day).- added a commit that references this issue
on Dec 24, 2022 Okay,
intgains the method in 3.12 now (PR merged). That seemed to be the most pressing issue.I expect less demand for
.is_integer()from Fraction and Decimal users?The problem adding it to
intaddressed was the practicality of what a: floattype annotation means given that type checkers intentionally define that to implicitly meanfloat|intto match the expectations of most of the worlds code. I'm unaware of anything implicitly unioning Fraction or Decimal along with float or int.Regardless, separate PRs make sense to consider for those.
- added a commit that references this issue
on Dec 28, 2022
Feature or enhancement
Function arguments and variables annotated with
floatshould not allow value of typeint.Pitch
PEP484 suggested that
when an argument is annotated as having type float, an argument of type int is acceptable;. This allows this kind of typing to be valid:x: float = 2But
intis not subtype offloatand doesn't provide the same interface. Float provides methods that are not available inint:is_integerfromhexhexThis violates LSP and is problematic especially with
is_integer:This method clearly states that it requires
floatand as an author of such code, I would expect thatis_integerwould be available if my typing is correct.There are workarounds (
if int(number) == number:) but they render theis_integeruseless as it can never be safely used.Just adding the missing methods to
int(or removing the extra methods fromfloat) would not be valid solution as there are other problems stemming from the fact thatintis notfloat. Eg.:I'm proposing an errata to PEP484 that will remove
when an argument is annotated as having type float, an argument of type int is acceptable;.Linked PRs