[Python-Dev] Symmetry arguments for API expansion (original) (raw)
Steven D'Aprano steve at pearwood.info
Wed Mar 21 08:38:32 EDT 2018
- Previous message (by thread): [Python-Dev] Symmetry arguments for API expansion
- Next message (by thread): [Python-Dev] Symmetry arguments for API expansion
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
On Wed, Mar 21, 2018 at 10:31:19AM +0000, Chris Barker wrote:
On Wed, Mar 21, 2018 at 4:42 AM Steven D'Aprano <steve at pearwood.info> wrote: > > Could float et al. add an index method that would return a ValueError > > if the value was not an integer? > > That would allow us to write things like: > > "abcdefgh"[5.0] > > which is one of the things index was invented to prevent.
I’m not so sure — it was invented to prevent using e.g. 6.1 as an index, which int(I) would allow.
As would int(6.0). If we wanted 6.0 to be accepted as an index, then floats would already have an index method :-)
[...]
But Guidos point is well taken — Having index fail based on value is setting people up for bugs down the line.
However, it seems use of isinteger() on a float is setting people up for exactly the same sorts of bugs.
I don't think so. You aren't going to stop people from testing whether a float is an integer. (And why should you? It isn't wrong to do so. Some floats simply are integer valued.) All you will do is force them to write code which is even worse than what they have now.
One wrong solution:
int(x) == x
That can raise ValueError and OverflowError, but at least it is somewhat understandable.
Serhiy suggested that people should use the cryptic:
(not x % 1.0)
but that's hardly self-documenting: its not obvious what it does or how it works. Suppose I see that snippet in a code review, and let's suppose I recognise it and aren't totally perplexed by it. Will it pass the review? I have to make a decision:
will it fail when x is an INF or NAN?
does it give the correct results when x is negative?
does this suffer from rounding errors that could affect the result?
what if x is not a float, but a Decimal, a Fraction or an int too big to convert to a float?
None of the answers are obvious at a glance.
In fact, Serhiy's suggestion is not correct when x is not a float:
py> from fractions import Fraction py> x =Fraction(1) + Fraction(1, 10**500) # certainly not an integer py> x.denominator == 1 # sadly Fraction doesn't support is_integer False py> not x % 1.0 True
Another example is that pow() functions sometimes swap to an exact > algorithm if the power is an int. There's no particular reason why > xn and xn.0 ought to be different, but they are: > > py> 123**10 > 792594609605189126649 > > py> 123**10.0 > 7.925946096051892e+20
I think this is exactly like the index use case. If the exponent is a literal, use what you mean.
Naturally. I already eluded to that in my earlier post. Nevertheless, this is just an example, and we shouldn't expect that the power will be a literal. I'm just illustrating the concept.
If the exponent is a computed float, then you really don’t want a different result depending on whether the computed value is exactly an integer or one ULP off.
I don't think you actually mean to say that. I'm pretty sure that we do want different results if the exponent differs from an integer by one ULP. After all, that's what happens now:
py> x = 25 py> x1.0 25.0 py> x(1.0+(2**-52)) # one ULP above 25.000000000000018 py> x**(1.0-(2**-53)) # one ULP below 24.99999999999999
I don't want to change the behaviour of pow(), but we shouldn't dismiss the possibility of some other numeric function wanting to treat values N.0 and N the same. Let's say, an is_prime(x) function that supports floats as well as ints:
is_prime(3.0) # True
is_prime(3.00001) # False
If the argument x.is_integer() returns True, then we convert to an int and test for primality. If not, then it's definitely not prime.
The user should check/convert to an integer with a method appropriate to the problem at hand.
Oh, you mean something like x.is_integer()? I agree!
wink
If it wasn’t too heavyweight, it might be nice to have some sort of flag on floats indicating whether they really ARE an integer, rather than happen to be:
-Created from an integer literal - created from an integer object - result of floor(), ceil() or round()
I don't understand this.
You seem to be saying that none of the following are "really" integer valued:
float(10) floor(10.1) ceil(10.1) round(10.1)
If they're not all exactly equal to the integer 10, what on earth should they equal?
-- Steve
- Previous message (by thread): [Python-Dev] Symmetry arguments for API expansion
- Next message (by thread): [Python-Dev] Symmetry arguments for API expansion
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]