Why Python 3?

Chris Angelico rosuav at gmail.com
Sat Apr 19 16:31:07 EDT 2014


On Sun, Apr 20, 2014 at 5:58 AM, Ian Kelly <ian.g.kelly at gmail.com> wrote:
> Considering that Fraction and Decimal did not exist yet, what type do
> you think the PEP 238 implementers should have chosen for the result
> of dividing two ints?  If float is not acceptable, and int is not
> acceptable (which was the whole point of the PEP), then the only
> alternative I can see would have been to raise a TypeError and force
> the user to upcast explicitly.  In that case, dividing arbitrary ints
> using floating-point math would not be possible for those ints that
> are outside the range of floats; you would get OverflowError on the
> upcast operation, regardless of whether the result of division would
> be within the range of a float.
>
>> Yes, I can see that it's nice for simple interactive use.
>
> More importantly, it's useful for implementers of generic mathematical
> routines.  If you're passed arbitrary inputs, you don't have to check
> the types of the values you were given and then branch if both of the
> values you were about to divide happened to be ints just because the
> division operator arbitrarily does something different on ints.

Or you just cast one of them to float. That way you're sure you're
working with floats.

The main trouble is that float is not a perfect superset of int. If it
were, then it really would be upcasting, same as turning a float into
a complex is; there's no downside, other than performance.

If I'd been in charge, I would have simply let int/int continue to
return an int, as that's the one thing that is guaranteed not to
behave differently on different input values. Python 3 fixed Unicode
handling by ensuring that mixing text and bytes would cause problems
straight away, rather than waiting until you get a character with a
codepoint higher than U+00FF; 3.3 went further and made sure you
wouldn't get problems by going past U+FFFF even on Windows. I think we
all agree (well, all bar the trolls) that that was a good thing. So
why do we have this sort of thing go weird?

def always_true(x):
    assert type(x) is int
    return x*10/2 == x*5

In Python 2, I believe that will indeed be always true, for any
integer x. (Yeah, there's a naughty type check in there. I'm talking
about integers, mmkay?) In Python 3, it might not be.

>>> always_true(2**53)
True
>>> always_true(2**53+1)
False

(32-bit Windows, because I'm on the laptop. Other Pythons, other CPUs,
etc, may have different points where that happens, but the same will
happen.)

So either you keep a very close eye on everything to make sure you
don't have floats infecting your calculations, or you ignore the
problem and then start seeing odd stuff happen with specific numbers.
I'd rather have to explicitly request floating-point division; that
way, you get issues a lot sooner and more simply. "Why is 34/10 equal
to 3?" is a lot easier to explain than "Why does my program not work
when I give it numbers with lots of data encoded in them, when it
works fine with sequential numbers from zero?". (Imagine if you work
with invoice numbers, for instance, and your code is fine; but if you
encode the date into the first eight digits, then put the store number
in the next three, register number in the next three, and then the
last three are sequential. Should work the same, right?)

Anyway, way too late to change now. That ship sailed in 2.2 or thereabouts.

ChrisA



More information about the Python-list mailing list