for / while else doesn't make sense

Chris Angelico rosuav at gmail.com
Fri May 20 22:05:12 EDT 2016


On Sat, May 21, 2016 at 10:35 AM, Jon Ribbens
<jon+usenet at unequivocal.co.uk> wrote:
> On 2016-05-20, Steven D'Aprano <steve at pearwood.info> wrote:
>> By that logic, we ought to:
>>
>> - avoid using floats because their behaviour isn't intuitive and
>>   can be confusing;
>
> To be fair, I'm very sympathetic to that argument. I think programming
> languages should never magically produce floats out of nowhere unless
> the programmer has explicitly done "import float" or "float('3.23')"
> or somesuch. They're misunderstood so often that any convenience
> they provide is outweighed by the danger they bring.
>
> "(1/10) * (1/10) * 10 != (1/10)" anyone? I was distinctly unhappy with
> the Python 3 "2/3 ~= 0.6666" thing and regard it as a very retrograde
> change.

The trouble is, what SHOULD 2/3 return?

* An integer? Makes a lot of sense to a C programmer. Not so much to
someone who is expecting a nonzero value. This isn't terrible (hey,
Python 2 managed with it no problem), but will definitely confuse a
number of people.

* A float? That's what we currently have. Not perfect, but it's going
to confuse less people than 0 will.

* A decimal.Decimal? That has its own issues (for instance, (x+y)/2
can return a value that isn't between x and y), and it still can't
represent two thirds properly.

* A fractions.Fraction? Well, at least that can perfectly represent a
ratio of integers. But it plays REALLY badly with other non-integer
types, and other operations than basic arithmetic (ever tried to take
the square root of a fraction?), so it's really only suited to
situations where you're working exclusively with fractions.

* Something else?

You say that Py3 making 1/10 => 0.1 was a "very retrograde change".
Why? Yes, now you're seeing floats; but would you be less surprised by
the Py2 version? Sure, Py2 has your little toy example working:

>>> (1/10) * (1/10) * 10 == (1/10)
True

but that's because 1/10 is zero, not because it's been represented
accurately! The biggest advantage of having integer division yield an
integer is that it forces people to think about their data types; but
since Python's float has a standing (core language support) that
Decimal and Fraction don't have, I don't think it's a problem. It's
the same with the complex type:

>>> 2 ** 0.5
1.4142135623730951
>>> (-2) ** 0.5
(8.659560562354934e-17+1.4142135623730951j)

Core data types will migrate between themselves as needed. (And this
proves some of the inherent inaccuracies in floating point arithmetic;
real number arithmetic says that the square root of -2 has a zero real
part.)

Interestingly, fractions.Fraction doesn't handle non-integer
exponentiation, and punts to float:

>>> fractions.Fraction(4) ** fractions.Fraction(2)
Fraction(16, 1)
>>> fractions.Fraction(4) ** fractions.Fraction(2, 3)
2.5198420997897464
>>> fractions.Fraction(4) ** fractions.Fraction(3, 2)
8.0
>>> fractions.Fraction(-4) ** fractions.Fraction(1, 2)
(1.2246467991473532e-16+2j)

Some data types just aren't well suited to certain operations.

ChrisA



More information about the Python-list mailing list