Why Python 3?

G

Gregory Ewing

Chris said:
I'd rather have to explicitly request floating-point division;

When you write / in Python 3, you *are* explicitly requesting
floating-point division.

Similarly, when you write // you're explicitly requesting
integer division.

I don't see the problem. You write whatever you mean and it
does what you tell it to do.
So either you keep a very close eye on everything to make sure you
don't have floats infecting your calculations,

If you have something that works exclusively on ints and someone
passes you a float, and you don't check for that, you'll have
problems anyway even if no division is involved at all.

There's no way that Python 3 division can *introduce* a float
into an integer calculation unless you write / somewhere where
you really meant //. But that's the same kind of mistake as
calling foo() when you meant to call bar(). You can't blame
the language for that.
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?)

It'll work fine as long as you use // when extracting the parts.
If you use / then you're *explicitly* saying to do the calculation
in floating point, which would not be a sane thing to do.
 
C

Chris Angelico

When you write / in Python 3, you *are* explicitly requesting
floating-point division.

Similarly, when you write // you're explicitly requesting
integer division.

I don't see the problem. You write whatever you mean and it
does what you tell it to do.

Truncating vs true is not the same as int vs float. If you mean to
explicitly request float division, you call float() on one or both
arguments. You're being explicit about something different.

ChrisA
 
G

Gregory Ewing

Chris said:
Is your function so generic that it has to be able
to handle float, Decimal, or complex, and not care about the
difference, and yet has to ensure that int divided by int doesn't
yield int?

It doesn't have to be that generic to cause pain. Even if
you're only dealing with floats, the old way meant you had
to stick float() calls all over the place in order to be
sure your divisions do what you want. Not only does that
clutter up and obscure the code, it's needlessy inefficient,
since *most* of the time they don't do anything.

There's also the annoyance that there's more than one
obvious way to do it. Do you write float(x)/y or
x/float(y)? Or do you go for a more symmetrical look
and write float(x)/float(y), even though it's redundant?

The new way makes *all* of that go away. The only downside
is that you need to keep your wits about you and select
the appropriate operator whenever you write a division.
But you had to think about that *anyway* under the old
system, or risk having your divisions silently do the
wrong thing under some circumstances -- and the remedy
for that was very clunky and inefficient.

I'm thoroughly convinced that the *old* way was the
mistake, and changing it was the right thing to do.
The language doesn't specify a means of resolving the conflict between
float and Decimal, but for some reason the division of two integers is
blessed with a language feature.

No, it's not. What the language does is recognise that
there are two kinds of division frequently used, and that
the vast majority of the time you know *when you write the
code* which one you intend. To support this, it provides two
operators. It's still up to the types concerned to implement
those operators in a useful way.

The built-in int and float types cooperate to make // mean
integer division and / mean float division, because that's
the most convenient meanings for them on those types.
Other types are free to do what makes the most sense for
them.
 
P

Paul Rubin

Terry Reedy said:
LibreOffice bundles 3.3. So anyone who does Python scripting in
LibreOffice is using Python 3. Actually, I believe LO uses Python
internally for some of its scripting. If so, everyone using LO is
indirectly using 3.3.

I didn't even know LO supported Python scripting, but I wouldn't count
such indirect use anyway. I meant I don't know any Python programmers
(at least in person) who use Python 3 for their daily coding. I think
this is mostly because they (and I) use whatever is in the OS distro,
and that is generally still 2.6 or 2.7.
 
T

Terry Reedy

When you write / in Python 3, you *are* explicitly requesting
floating-point division.

Similarly, when you write // you're explicitly requesting
integer division.

One is requesting 'floor division'
1.0

To me, calling that integer division is a bit misleading in that one
might expect the result, at least, to be an int rather than a float.
(Yes, it is an integer-valued float.)
 
S

Steven D'Aprano

It's just that the improvement
from 2 to 3 is rather small, and 2 works perfectly well and people are
used to it, so they keep using it.

Spoken like a true ASCII user :)

The "killer feature" of Python 3 is improved handling of Unicode, which
now brings Python 3 firmly into the (very small) group of programming
languages with first-class support for more than 128 different characters
by default.

Unfortunately, that made handling byte strings a bit more painful, but
3.4 improves that, and 3.5 ought to fix it. People doing a lot of mixed
Unicode text + bytes handling should pay attention to what goes on over
the next 18 months, because the Python core developers are looking to fix
the text/byte pain points. Your feedback is wanted.

There are nice tools that help port
your codebase from 2 to 3 with fairly little effort. But, you can also
keep your codebase on 2 with zero effort. So people choose zero over
fairly little.

True. But for anyone wanting long term language support, a little bit of
effort today will save them a lot of effort in six years time.
 
S

Steven D'Aprano

One of the problems is you don't know in advance if something is going
to stop you. By committing to P3 now, you are eliminating from possible
future use, all of those third-party modules which only support P2. And
you don't know which of those you'll need until you sometime in the
future.

I believe this is more of an issue in theory than in practice. My on-line
web portal app could potentially use any of a thousand different Python
2.x only libraries, but in practice I'm probably only going to use half a
dozen libraries, or fewer, and know very early in the design phase what I
need (web templating software, yes, library to control radio telescopes,
probably not). And I'm likely to eliminate from contention most libraries
that seem unsupported or abandoned, and if they only support Python 2
five years since the introduction of Python 3, they better have a good
reason for it.
 
S

Steven D'Aprano

If float were a perfect superset of int, and the only logical superset
when you want non-integers, then it'd be fine. But if you're mixing int
and Decimal, you have to explicitly convert,

I don't think so. Operations on mixed int/Decimal arguments return
Decimal. There's no conversion needed except to get the original Decimal
number in the first place. (Decimal is not a built-in and there's no
literal syntax for them.)

py> from decimal import Decimal as D
py> x, y = 1, D(2)
py> x/y
Decimal('0.5')
py> x//y
Decimal('0')

whereas if you're mixing
int and float, you don't. Why is it required to be explicit with Decimal
but not float? Of all the possible alternate types, why float? Only
because...

Because float is built-in and Decimal is not. Because Decimal wasn't
introduced until Python 2.4 while the change to the division operator was
first begun back in Python 2.2.

http://python.org/dev/peps/pep-0238/

Guido writes about why his decision to emulate C's division operator was
a mistake:

http://python-history.blogspot.com.au/2009/03/problem-with-integer-division.html

... it already existed. There's no particular reason to up-cast to
float, specifically, and it can cause problems with large integers -
either by losing accuracy, or by outright overflowing.

If you reject C integer division, you have to do *something* with 1/2.
Ideally you'll get a number numerically equal to one half. It can't be a
Decimal, or a Fraction, because back in 2001 there were no Decimal or
Fraction types, and even now in 2014 they aren't built-ins.

(Besides, both of those choices have other disadvantages. Fractions are
potentially slow and painful, with excessive accuracy. See Guido's
comments in his blog post above. And Decimal uses base 10 floating point,
which is less suitable for serious numeric work than base 2 floats.)

Suppose you take an integer, multiply it by 10, and divide it by 5. In
theory, that's the same as multiplying by 2, right?

That's a bit of a contrived example. But go on.

Mathematically it
is. In C it might not be, because the multiplication might overflow; but
Python, like a number of other modern languages, has an integer type
that won't overflow.

Only since, um, version 2.2 I think. I don't have 2.1 easily available,
but here's 1.5:


[steve@ando ~]$ python1.5
Python 1.5.2 (#1, Aug 27 2012, 09:09:18) [GCC 4.1.2 20080704 (Red Hat
4.1.2-52)] on linux2
Copyright 1991-1995 Stichting Mathematisch Centrum, AmsterdamTraceback (innermost last):
File "<stdin>", line 1, in ?
OverflowError: integer pow()


So don't forget the historical context of what you are discussing.


In Python 2, doing the obvious thing works:

x * 10 / 5 == x * 2

Ignoring old versions of Python 2.x, correct. But that is a contrived
example. Mathematically x/5*10 also equals 2*x, but not with C division
semantics. This is with Python 2.7:

(10, 14, 14)

Let's try it with true (calculator) division:
(14.0, 14.0, 14)


With a bit of effort, I'm sure you can find values of x where they are
not all equal, but that's because floats only have a finite precision. In
general, true division is less surprising and causes fewer unexpected
truncation errors.

In Python 3, you have to say "Oh but I want my integer division to
result in an integer":

x * 10 // 5 == x * 2

No, // doesn't mean "divide and coerce to an integer", it is
*truncating* division. The type being truncated may choose to return
an int, but that's not compulsory:

(Decimal('5.875'), Decimal('5'))

Yes, I can see that it's nice for simple interactive use. You type "1/2"
and you get back 0.5. But doesn't it just exchange one set of problems
("dividing integers by integers rounds")

It doesn't round, it truncates.

[steve@ando ~]$ python2.7 -c "print round(799.0/100)"
8.0
[steve@ando ~]$ python2.7 -c "print 799/100"
7

for another set ("floating
point arithmetic isn't real number arithmetic")?

It's not like that avoiding that problem is an option.
 
J

Jussi Piitulainen

Steven said:
It doesn't round, it truncates.

[steve@ando ~]$ python2.7 -c "print round(799.0/100)"
8.0
[steve@ando ~]$ python2.7 -c "print 799/100"
7

Seems it floors rather than truncates:

$ python2.7 -c "from math import trunc;print trunc(799./-100)"
-7
$ python2.7 -c "from math import floor;print floor(799./-100)"
-8.0
$ python2.7 -c "print 799/-100"
-8

$ python3.2 -c "print(799//-100)"
-8
 
S

Steven D'Aprano

Steven said:
It doesn't round, it truncates.

[steve@ando ~]$ python2.7 -c "print round(799.0/100)" 8.0
[steve@ando ~]$ python2.7 -c "print 799/100" 7

Seems it floors rather than truncates:

$ python2.7 -c "from math import trunc;print trunc(799./-100)" -7
$ python2.7 -c "from math import floor;print floor(799./-100)" -8.0
$ python2.7 -c "print 799/-100"
-8

$ python3.2 -c "print(799//-100)"
-8


Ah yes, so it does. Sorry for the confusion. This has been reported as a
bug at least twice, but it is actually working as intended. // floors,
not truncates towards zero.

http://bugs.python.org/issue19446
http://bugs.python.org/issue19574
 
I

Ian Kelly

In that case, you already have a special case in your code, so whether
that special case is handled by the language or by your code makes
little difference. Is your function so generic that it has to be able
to handle float, Decimal, or complex, and not care about the
difference, and yet has to ensure that int divided by int doesn't
yield int? Then say so; put in that special check. Personally, I've
yet to meet any non-toy example of a function that needs that exact
handling; most code doesn't ever think about complex numbers, and a
lot of things look for one specific type:

When I'm writing a generic average function, I probably don't know whether
it will ever be used to average complex numbers. That shouldn't matter,
because I should be able to rely on this code working for whatever numeric
type I pass in:

def average(values):
return sum(values) / len(values)

This works for decimals, it works for fractions, it works for complex
numbers, it works for numpy types, and in Python 3 it works for ints.
Maybe it's not your code that should be caring about what happens when
you divide two integers, but the calling code. If you're asking for
the average of a list of numbers, and they're all integers, and the
avg() function truncates to integer, then the solution is to use sum()
and explicitly cast to floating point before dividing.

First, that's not equivalent. Try the following in Python 3:

values = [int(sys.float_info.max / 10)] * 20
print(average(values))

Now try this:

print(average(map(float, values)))

I don't have an interpreter handy to test, but I expect the former to
produce the correct result and the latter to raise OverflowError on the
call to sum.

Second, why should the calling code have to worry about this implementation
detail anyway? The point of a generic function is that it's generic.
 
M

MRAB

complex.

In that case, you already have a special case in your code, so whether
that special case is handled by the language or by your code makes
little difference. Is your function so generic that it has to be able
to handle float, Decimal, or complex, and not care about the
difference, and yet has to ensure that int divided by int doesn't
yield int? Then say so; put in that special check. Personally, I've
yet to meet any non-toy example of a function that needs that exact
handling; most code doesn't ever think about complex numbers, and a
lot of things look for one specific type:

When I'm writing a generic average function, I probably don't know
whether it will ever be used to average complex numbers. That shouldn't
matter, because I should be able to rely on this code working for
whatever numeric type I pass in:

def average(values):
return sum(values) / len(values)

This works for decimals, it works for fractions, it works for complex
numbers, it works for numpy types, and in Python 3 it works for ints.
Maybe it's not your code that should be caring about what happens when
you divide two integers, but the calling code. If you're asking for
the average of a list of numbers, and they're all integers, and the
avg() function truncates to integer, then the solution is to use sum()
and explicitly cast to floating point before dividing.

First, that's not equivalent. Try the following in Python 3:

values = [int(sys.float_info.max / 10)] * 20
print(average(values))

Now try this:

print(average(map(float, values)))

I don't have an interpreter handy to test, but I expect the former to
produce the correct result and the latter to raise OverflowError on the
call to sum.

Python 3.4.0 (v3.4.0:04f714765c13, Mar 16 2014, 19:25:23) [MSC v.1600 64
bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information..... return sum(values) / len(values)
....
values = [int(sys.float_info.max / 10)] * 20
print(average(values)) 1.7976931348623158e+307
print(average(map(float, values)))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>

In fact, that's true back to Python 3.1
 
C

Chris Angelico

When I'm writing a generic average function, I probably don't know whether
it will ever be used to average complex numbers.

This keeps coming up in these discussions. How often do you really
write a function that generic? And if you do, isn't it doing something
so simple that it's then the caller's responsibility (not the
function's, and not the language's) to ensure that it gets the right
result?

ChrisA
 
R

Roy Smith

Chris Angelico said:
This keeps coming up in these discussions. How often do you really
write a function that generic? And if you do, isn't it doing something
so simple that it's then the caller's responsibility (not the
function's, and not the language's) to ensure that it gets the right
result?

ChrisA

Hmmm. Taking the average of a set of complex numbers has a reasonable
physical meaning. But, once you start down that path, I'm not sure how
far you can go before things no long make sense. What's the standard
deviation of a set of complex numbers? Does that even have any meaning?
 
T

Terry Reedy

Hmmm. Taking the average of a set of complex numbers has a reasonable
physical meaning. But, once you start down that path, I'm not sure how
far you can go before things no long make sense. What's the standard
deviation of a set of complex numbers? Does that even have any meaning?

One can either calculate variance from the sum of squared distances from
the mean point, or calculate x and y deviations separately and calculate
the covariance matrix thereof.
 
G

Gregory Ewing

Chris said:
Truncating vs true is not the same as int vs float. If you mean to
explicitly request float division, you call float() on one or both
arguments. You're being explicit about something different.

If you know you're dealing with either ints or floats,
which is true in the vast majority of cases, then you
know that / will always perform float division.

As for why int/int should yield float and not some
other type, float is alreay special -- it's built-in
and has syntactic support in the form of literals.
It's the most obvious choice.

If a version of Python were ever to exist in which
floating-point literals produced Decimals instead of
floats, then int/int would produce a Decimal.
 
G

Gregory Ewing

Terry said:
One is requesting 'floor division'

1.0

In general that's true, but I'm talking about a context
in which you have some expectations as to the types of the
operands.

Most of the time, there are two possible scenarios:

1) The algorithm operates on integers, and the contract is
that you only get passed ints. In that case, you use //
and know that the result will be an int.

2) The algorithm operates on non-integers, and the contract
is that you get passed either ints or floats, with ints being
understood as standing in for floats. In that case, you
use / and know that it will perform float division and
return a float.

If someone passes you a float in case (1) it's true that
// won't return an int, but then they've violated the
contract.
 
C

Chris Angelico

If you know you're dealing with either ints or floats,
which is true in the vast majority of cases, then you
know that / will always perform float division.

And that's what I mean about the common non-trivial case. It's easy
enough to come up with contrived or trivial cases that use any types,
but in most cases, it'd be fine to explicitly call float() on one of
the operands to explicitly request floating-point division. Choosing
between two division operators is not the same thing as choosing a
data type.

Explicitly choosing float division:

x / float(y)

Explicitly choosing to truncate:

math.trunc(x / y)

Both explicit forms can be done cleanly without empowering the
language with the magic of int/int->float.

ChrisA
 
G

Gregory Ewing

I don't see why that's such a big hardship.

There are clear advantages to having an explicit way to
request non-floor division. Whatever way is chosen for
that, some other way has to be chosen to request floor
division.

One could debate whether it would have been better to make
/ mean floor division and invent something else for
non-floor division, but then some people would complain
"Oh, but I have to say I want my float division to return
a float!"

Either way requires people to make some changes in their
habits.
 
G

Gregory Ewing

Ian said:
def average(values):
return sum(values) / len(values)

This works for decimals, it works for fractions, it works for complex
numbers, it works for numpy types, and in Python 3 it works for ints.

That depends on what you mean by "works". I would actually
find it rather disturbing if an average() function implicitly
used floor division when given all ints.

The reason is that people often use ints as stand-ins for
floats in computations that are conceptually non-integer.
So a general-purpose function like average(), given a list
of ints, has no way of knowing whether they're intended
to be interpreted as ints or floats.

To my way of thinking, floor division is a specialised
operation that is only wanted in particular circumstances.
It's rare that I would actually want it done in the
context of taking an average, and if I do, I would rather
be explicit about it using e.g. int(floor(average(...))
or a specialised int_average() function.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top