
Why Python 3 Exists - cocoflunchy
http://www.snarky.ca/why-python-3-exists
======
andrewstuart
IMO one of the reasons for all the angst is that .encode() and .decode() are
so ambiguous and unintuitive which makes them incredibly confusing to use.
Which direction are you converting? From what to what? The whole unicode thing
is hard enough to understand without Python's encoding and decoding functions
adding to the mystery. I still have to refer to the documentation to make sure
I'm encoding or decoding as expected.

I think there would have been much less of a problem if encode and decode were
far more obvious, unambiguous and intuitive to use. Probably without there
being two functions.

Still a problem of course today.

~~~
dietrichepp
Hm, I never saw this as ambiguous at all, except for a few weird encodings
that Python has as "convenience" method.

Here's how you remember it: "Unicode" is _not_ an encoding. It never was, it
never will be. Of course, the data must be encoded in memory somehow, but in
Python 3, you cannot be sure what encoding that is because it's not really
exposed to the user. From what I understand, there are different encodings
that string objects will use, transparently, in order to save memory!

You always "encode" something into bytes, and "decode" bytes back into
something. There should be exactly two functions, because the functions have
different types: "encode" is str -> bytes, "decode" is bytes -> str. Explicit
is better than implicit.

    
    
        output = input.decode(self.coding)
    

With Python 3, I instantly know that "input" is bytes and "output" is str.

~~~
twoodfin
Indeed, the Python 3 Unicode string object is fascinatingly clever. Code worth
reading:

[https://github.com/python/cpython/blob/master/Objects/unicod...](https://github.com/python/cpython/blob/master/Objects/unicodeobject.c)

~~~
nostrademons
Also, it's incompatible with UTF-8 strings stored in C, which means that when
you cross the Python/C API boundary, you have to re-encode all strings. This
is a large performance penalty right at the time when you can least afford
performance penalties.

IMNSHO, most modern languages should be storing strings as UTF-8 and give up
on random access by characters. You almost never need it; in the most frequent
case where you do (using indexOf or equivalent to search for a substring, and
then breaking on it), you can solve the problem by returning a type-safe
iterator or index object that contains a byte offset under the hood, and then
slicing on that. Go, Rust, and Swift have all gone this route.

~~~
shoyer
This design doc from DyND (a possible NumPy alternative) has some useful
references on this point:
[https://github.com/libdynd/libdynd/blob/master/docs/string-d...](https://github.com/libdynd/libdynd/blob/master/docs/string-
design.md#code-unit-api-not-code-point)

~~~
rurban
Nope. This is just a normal short-string implementation, inlined with 64bit
systems, with a tag bit. Very common on every better VM.

This has nothing to do with (inefficient) encodings, and their bad historic
namings, probably derived from perl.

Of course any name like byte2utf8 or just to_byte or b2u8 would have been
better than encode/decode.

And of course cache size matters nowadays much more than immediate substring
access for utf8, so nobody should use ucs-2 or even ucs-4 internally. This is
easily benchmarkable.

~~~
0x0
"Byte2utf8" is a pretty confusing name for a method, considering utf8 is a
byte encoding of unicode... :)

------
Animats
Unicode worked just fine by Python 2.6. I had a whole system with a web
crawler and HTML parsers which did everything in Unicode internally. You had
to use "unicode()" instead of "str()" in many places, but that wasn't a
serious problem.

By Python 2.7, there were types "unicode", "str", and "bytes". That made
sense. "str" and "bytes" were still the same thing, for backwards
compatibility, but it was clear where things were going. The next step seemed
to be a hard break between "str" and "bytes", where "str" would be limited to
0..127 ASCII values. Binary I/O would then return "bytes", which could be
decoded into "unicode" or "str" when required. So there was a clear migration
path forward.

Python 3 dumped in a whole bunch of incompatible changes that had nothing to
do with Unicode, which is why there's still more Python 2 running than Python
3. It was Python's Perl 6 moment.

From the article: _" Obviously it will take decades to see if Python 3 code in
the world outstrips Python 2 code in terms of lines of code."_ Right. Seven
years in, Python 2.x still has far more use than Python 3. About a year ago, I
converted a moderately large system from Python 2 to Python 3, and it took
about a month of pain. Not because of the language changes, but because the
third-party packages for Python 3 were so buggy. I should not have been the
one to discover that the Python connector for MySQL/MariaDB could not do a
"LOAD DATA LOCAL" of a large data set. Clearly, no one had ever used that code
in production.

One of the big problems with Python and its developers is that the core
developers take the position that the quality of third party packages is
someone else's problem. Python doesn't even have a third party package
repository - PyPI is a link farm of links to packages elsewhere. You can't
file a bug report or submit a patch through it. Perl's CPAN is a repository
with quality control, bug reporting, and Q/A. Go has good libraries for most
server-side tasks, mostly written at Google or used at Google, so you know
they've been exercised on lots of data.

That "build it and they will convert" attitude and the growth of alternatives
to Python is what killed Python 3.

~~~
pwang
> That "build it and they will convert" attitude and the growth of
> alternatives to Python is what killed Python 3.

Well said.

------
danso
> _We have decided as a team that a change as big as unicode /str/bytes will
> never happen so abruptly again. When we started Python 3 we thought/hoped
> that the community would do what Python did and do one last feature release
> supporting Python 2 and then cut over to Python 3 development for feature
> development while doing bugfix releases only for the Python 2 version._

I'm guessing it's not a coincidence that string encoding was also behind the
Great Sadness of Moving From Ruby 1.8 to 1.9. How have other mainstream
languages made this jump, if it was needed, and were they able to do it in a
non-breaking way?

[https://news.ycombinator.com/item?id=1162122](https://news.ycombinator.com/item?id=1162122)

~~~
bdarnell
C and C++ are so widely used that transitions like this are made not at the
language level but at the level of platforms or other communities. Some parts
of the C/C++ world made this transition relatively seamlessly, while others
got caught in the same traps as Python.

The key is UTF-8: UTF-8 is a superset of 7-bit ASCII, so as long as you only
convert to/from other encodings at the boundaries of your system, unicode can
be introduced to the internal components in a gradual and mostly-compatible
way. You only get in trouble when you decide that you need separate "byte
string" and "character string" data types (which is generally a mistake: due
to the existince of combining characters, graphemes are variable-width even if
you're using strings composed of unicode code points, so you don't gain much
by using UCS-4 character strings instead of UTF-8 byte strings).

My theory is that the python 3 transition would have gone _much_ smoother and
still accomplished its goals if they had left the implicit str/bytes
conversion in place but just made it use UTF-8 instead of ASCII (although in
environments like Windows where UTF-16 is important this may not have worked
out as well).

~~~
dietrichepp
You are correct that there's no real benefit in UTF-32 over UTF-8, which is
why Go and Rust (and others) have worked with UTF-8 in memory just fine.
However, the actual encoding of a str object is irrelevant, and it's not the
point. The whole point of the str/bytes difference in Python is that you make
Python keep track of whether you've done the conversion or not. In Python 2,
you can be sloppy, and the programs are buggy as a result!

You're putting the cart before the horse here in terms of the Python 3
transition. The str/unicode fix was one of the driving factors for Python 3 to
exist in the first place, and if you removed it, then what's the point?

Again, look at Go or Rust. Both of them have separate types for strings and
bytes, even though they have the same representation in memory, and as a
result we don't have that kind of bug in our program.

~~~
bdarnell
> The str/unicode fix was one of the driving factors for Python 3 to exist in
> the first place, and if you removed it, then what's the point?

The reason python 3 exists is that most python 2 code had latent unicode-
related bugs that would only manifest when they were exposed to non-ascii
data. The backwards-incompatible barrier between str and bytes was the
solution the python 3 team chose for this problem; adopting utf-8 as the
standard encoding would have been another solution which I claim would have
been more backwards-compatible (essentially moving to the go/rust model, which
prove that you don't necessarily need separate byte and character string types
for correct unicode handling).

~~~
hetman
But not every 8-bit byte string is valid UTF-8 so that could still cause a
world of pain.

------
tzs
In the Reddit discussion of this, someone linked to this criticism [1] of
Python 3's Unicode handling written by Armin Ronacher, author of the Flask
framework.

I am not competent to say whether this is spot on or rubbish or somewhere in
between [2], but it seemed interesting at least.

[1] [http://lucumr.pocoo.org/2014/5/12/everything-about-
unicode/](http://lucumr.pocoo.org/2014/5/12/everything-about-unicode/)

[2] Almost all of my Python 2 experience is in homework assignments in MOOCs
for problems where there was no need to care about whether strings were ASCII,
UTF-8, binary, or something else. My Python 3 experience is a handful of small
scripts in environments where everything was ASCII.

~~~
lmm
It's wrong. The whole point of cat is to concatenate files. But if you
concatenate two files with different encodings, you end up with an unreadable
file. So you _want_ cat to error out if one of the files that was passed has a
different encoding from the encoding you told it to use, _which is exactly
what the python cat will do_.

~~~
jlg23
> The whole point of cat is to concatenate files.

Yes.

> So you want cat to error out if one of the files that was passed has a
> different encoding.

No. I expect it to read bits from stream A until it is exhausted and then read
bits from stream B until it is exhausted. All the time just writing the ones
and zeros read to the output stream (of bits). And no, a byte does not have to
be "8 bit"
([http://www.lispworks.com/documentation/HyperSpec/Body/f_by_b...](http://www.lispworks.com/documentation/HyperSpec/Body/f_by_by.htm)).

~~~
lmm
And concatenating a file of 9-bit bytes with a file of 8-bit bytes will
produce something useful? No. If you don't know what your bits represent then
you will corrupt them. Python does not need to faithfully reproduce all the
historical oddities of unix.

~~~
jlg23
It might - depending on what I intend to do with the file (I still have the
offsets of the individual files because I know the original file sizes).

API-wise, in my humble experience, it's hell to deal with operations that are
supposed to work on bit-streams but try to be smart and ask me for encodings
of those - this is information I might not even have when building on those
basic operations. The "oddities", how you call them, are the result of not
over-abstracting the code to handle yet-unknown problems.

You want to concatenate text files with different encodings? _Convert_ them.
Expecting a basic tool to do this for you (or carp on "problems")
quintessentially leads to cat converting image formats for you and demanding
to know how to _combine_ those images: (addition, subtraction, append to
left/top/right/bottom of first image etc).

~~~
lmm
> API-wise, in my humble experience, it's hell to deal with operations that
> are supposed to work on bit-streams but try to be smart and ask me for
> encodings of those - this is information I might not even have when building
> on those basic operations. The "oddities", how you call them, are the result
> of not over-abstracting the code to handle yet-unknown problems.

That's how C ended up as the security nightmare that it is. There are a lot of
things you can do in C that you can't do in Python - you can reinterpret
strings or floats as integers, you can subtract pointers from integers, you
can write to random memory addresses.... Sometimes these things are useful,
but most of the time they just lead to your program breaking in an unhelpful,
nonobvious way.

Python is not that kind of language; it will go to some pains to help you not
make mistakes. If you want to do bit-twiddling in Python there are APIs for
it, and you could implement a "bit-level cat" using them, but it's never going
to be the primary use case. Arguably there should be better support for
accessing stdin/stdout in binary mode, but that would make it very easy to
accidentally interleave text and binary output which would again result in
(probably silent) corruption. (Writing a "binary cat" that concatenates two
_files_ of bytes would not lead to any of the problems in the linked article -
it's only trying to use stdin/stdout that's causing the trouble in the link).

~~~
jlg23
> That's how C ended up as the security nightmare that it is.

And that's how I ended up completely wasted after a friend had to throw a
party after a GNU version of a common unix tool was able to accept his first
name as valid parameters.

And no, C's problem are _not_ based on encoding issues. Those are not even a
first-class symptom.

> Python is not that kind of language

Maybe. I don't care much, even though I do speak Python fluently. Though, I
_do care_ about minimal functional units whose documentation I can grasp in
minutes, not hours.

> [Python] will go to some pains to help you not make mistakes.

This is not specific to python, this is specific to [language] developers. If
all you do is text processing, you will think in characters and their
encoding. I don't. A lot of programmers don't - because they deal with real
world data that is almost never most efficiently encoded in text.

To reiterate: We are all dealing with bit-streams. Semantics of those are
specific to their context. If your context is "human readable text" \- deal
with it. But please don't make me jump through hoops if I actually just want
to deal with bit-streams. If you need magic to make your specific use-case
easier, wrap the basic ops in a library and use it.

Last but not least: This all is completely off-topic when the question is
about "why python3" \- it's great for your use-case, but from an abstract
point of view, python3 was just the rational continuation of python2, cleaning
up a lot of inherited debt. Though it might fit your world-view, it's not
necessarily what it was about.

------
rkrzr
IMO the biggest reason to use Python3 is its concurrency support via async +
await.

Fixing the unicode mess is nice too of course, but you can get most of the
benefits in Python2 as well, by simply putting this at the top of all of your
source files:

from __future__ import unicode_literals

Also make sure to _decode_ all data from the outside as early as possible and
only _encode_ it again when it goes back to disk or the network etc.

~~~
tudborg
So much this! asyncio was the main selling point for me, but in general, why
not follow the language?

I never really understood the "rather stay with py2.7" thing. I get it with
big old monolithic applications. You don't "just" rewrite those, but _every
new python project_ should be done with the latest stable release.

Is anyone starting their PHP projects on PHP4? Any new node projects in 0.10?
Of course not, that would be moronic.

~~~
netheril96
Because you don't know what libraries you may depend on in the future when you
start a new project. I was a fervent Python 3 supporter, and wrote every of my
fresh projects in Python 3 instead of 2, until one day I found I needed LLVM
in my project, yet the python port at that time was for 2 only.

I mostly avoid writing Python 3 nowadays, because I don't want to rewrite my
project or find painstakingly an alternative solution when I could have just
imported a module that runs fine under Python 2.

------
BuckRogers
I chose to port from CPython2 to PyPy4, rather than to CPython3. It just made
more sense. I for one see no value in Python3 (unicode has been supported
since 2.6). My reasons for migrating to PyPy4 instead of Python3-

1) It was easier than porting to CP3.

2) It gave me a tangible benefit by removing all CPU performance worries once
and for all. Added "performance" as a feature for Python. Worth the testing
involved.

3) It removed the GIL. If you use PyPy4 STM, which is currently a separate
JIT. Which will be at some point merged back into PyPy4.

So for me, Python3 can't possibly compete, and likely never will with PyPy4
once you consider the performance and existing code that runs with it. PyPy3
is old, beta, not production-ready, based on 3.2 and Py3 is moving so fast I
don't think PyPy3 would be able to keep up if they tried.

Python3 is dead to me. There's not enough value for a new language. I'm not
worried about library support because Py2 is still bigger than 3 and 2.7 will
be supported by 3rd party libraries for a very long time else choose
irrelevance (Python3 was released in 2008, and still struggling to justify its
existence...). My views on the language changes themselves are stated much
better by Mark Lutz[0]. I'm more likely to leave Python entirely for a new
platform than I am to migrate to Python3.

PyPy is the future of Python. If the PyPy team announces within the next 5
years they're taking the mantle of Python2, that would be the nail in the
coffin. All they have to do is sit back and backport whatever features the
Python2/PyPy4 community wants into PyPy4 from CPython3 as those guys run off
with their experiments bloating their language. I believe it's all
desperation, throwing any feature against the wall. Yet doing irreparable harm
bloating the language, making the famous "beginner friendly" language the
exact opposite.

I already consider myself a PyPy4 programmer, so I hope they make it an
official language to match the implementation. There's also Pyston to keep an
eye on which is also effectively 2.x only at this time.

[0][http://learning-python.com/books/python-
changes-2014-plus.ht...](http://learning-python.com/books/python-
changes-2014-plus.html)

~~~
cname
There's just a bit of hyperbole in your comment. Most major libraries have
been ported to Python 3. I wonder if the opposite of what you're saying will
happen--i.e, the libraries that don't support Python 3 will be left behind.
Fabric is an example of that for me.

~~~
BuckRogers
Of course you may be right, but obviously I don't think so at this time. I
feel I made a smart, safe bet. I'm still writing valid 2.7 code that could be
migrated to 3.x at any point if for any reason my current plan would fall
through. It would be just as easy for me to migrate to 3.x as it would anyone
else with 2.x codebases.

So far, solving CPU performance in Python and removing the GIL is pretty much
the holy grail. Python2 would have to completely collapse (no signs of this)
for CPython3's ecosystem to outweigh the long-tail of libraries only on 2.x
and the truly next-level dynamic language CPU performance.

While I am enjoying "performance as a feature" and GIL-free Python at the
moment, I can still migrate to Python3 at any time if I lose my safe bet with
PyPy4/Py2.

To me it looked and still looks like a no brainer.

~~~
cname
> I'm still writing valid 2.7 code that could be migrated to 3.x at any point

Well, that's kinda true, although there is somewhat of a learning curve doing
2-to-3 migrations. Depending on your situation, this may or may not matter,
but if you need to "move fast" on this at some point, it'd at least be
beneficial to know what's involved (even though it's not that onerous IMO).

~~~
BuckRogers
I've used (tested) Python3 many times over the years and releases. I'd
consider the changes trivial. I'm not anti-Python3, I'm pro-PyPy4.

In my attempts to test Python3 releases though I've ran across bugs and
performance regressions from 2.x. After I continually ran into this in a few
releases I eventually threw my hands up. The last release I ran tests on was
3.4 and I'm no longer interested in later releases until something as
substantial as PyPy's CPU performance and removal of the GIL (PyPySTM) shows
up to beat what I have now.

It's slick to have the entire Python2 ecosystem, major performance boost to
your code, and still leave the door open for Python3 if they ever stop
bloating the language.

Other than being dramatically slower than PyPy4, Python3 is also feature soup.
I strongly dislike technical churn rather than true technical innovation
(which is what PyPy represents). I'm more in line philosophically with Go, I'd
prefer to remove features until you're down to a very concise and stable core.
Python3 has many negatives, but from my perspective they keep piling on more.

------
rdslw
I love when people with native english skills write monsters like this: "If
people's hopes of coding bug-free code in Python 2 actually panned out then I
wouldn't consistently hear from basically every person who ports their project
to Python 3 that they found latent bugs in their code regarding encoding and
decoding of text and binary data."

This should be under penalty ;)

Anyone to divide it into few simpler sentences?

UPDATE: And another one from our connected sentences loving author: "We
assumed that more code would be written in Python 3 than in Python 2 over a
long-enough time frame assuming we didn't botch Python 3 as it would last
longer than Python 2 and be used more once Python 2.7 was only used for legacy
projects and not new ones."

~~~
teek
The first one:

> If people's hopes of coding bug-free code in Python 2 actually panned out

Python2 developers wanted to write bug-free code _.

_ code = for the purpose of processing text and binary data

> then I wouldn't consistently hear from basically every person

Python2 developers could not write bug free code. So they complained _.

_ complained = complained about their algorithms having bugs when they rewrote
those algorithms in Python3

> that they found latent bugs in their code regarding encoding and decoding of
> text and binary data.

Python2 code written by the same developers had bugs that they did not know
about.

When the same developers rewrote their code in Python3, they found the bugs.

(If Python3 did not exist, then it would be very hard to write bug-free code
in Python2.)

The second one:

> We assumed that more code would be written in Python 3 than in Python 2 over
> a long-enough time frame assuming we didn't botch Python 3 as it would last
> longer than Python 2 and be used more once Python 2.7 was only used for
> legacy projects and not new ones.

If we designed Python 3 correctly, then we expect Python 3 to live longer than
Python 2. We also expect more code to be written in Python 3 for the same
reason. We also expect only old projects will be written in Python 2.7.

------
Scarbutt
Since python3 is not backwards compatible with python2, why didn't the python
devs leverage the opportunity for creating a more performant non-GIL runtime
for python3?

~~~
svisser
Removing the GIL is a difficult problem: [http://python-
notes.curiousefficiency.org/en/latest/python3/...](http://python-
notes.curiousefficiency.org/en/latest/python3/multicore_python.html)

~~~
criddell
I think the question was if you aren't going to be backwards compatible, why
not unshackle yourself completely and design a new language without the GIL
and make it as pythonic as possible?

A scripting language that support multi-threading is possible, right? I think
TCL does it.

~~~
chrisseaton
I guess they were happy with breaking backwards compatibility a little bit,
not not as much as simply removing the GIL and not adding back any implicit
synchronisation at all.

------
nulltype
So Python 2 did not have super obvious string handling. One of the odd things
that they seemingly could have fixed pretty easily is to change the default
encoding from 'ascii' to 'utf8'. That would have fixed a bunch of the
UnicodeDecodeErrors that were the most obvious problem with strings:
[http://www.ianbicking.org/illusive-
setdefaultencoding.html](http://www.ianbicking.org/illusive-
setdefaultencoding.html)

If they had to make Python 3 anyway, I think the main thing they were missing
is that they should have added a JIT. That makes upgrading to Python 3 a much
easier argument. If the only point of the JIT was to add a selling point to
Python 3, that probably would have been worth it.

------
collinmanderson
It seems to me if bytes/unicode was the only breaking change we would probably
be over the transition by now.

There are a lot of other subtle changes that makes the transition harder:
comparison changes and keys() being an iterator for example. These are good
long term changes, but I wish they weren't bundled in with the bytes/unicode
changes.

------
cft
We migrated to Go from Python 2, since instead of incompatible Python 3 we
needed faster Python 2 replacement.

------
diimdeep
Str is tip of the iceberg. Python before 2.7 and current Python is completely
different language semantically; methods, functions, statements, expressions,
Global interpreter lock behavior.. This is sad that this blog post and
discussions around it didn't mention anything about it.

~~~
rcthompson
The article isn't covering all the differences between Python 2 and Python 3.
Based on this article as well as other articles I've read in the past, the
Unicode issue was the original reason they decided it was necessary to break
backward-compatibility, but once that decision was made, there was no reason
not to make any further backward-incompatible improvements.

------
PythonicAlpha
The reason, I still did not port to Python 3:

(and yes, Unicode in Py2 is a mess ...)

They just broke to many things (unnecessarily!) internally. Particularly they
changed many C APIs for enhancement modules, so that all of them had to be
ported, before they could be used with Python 3. They did not even consider a
portability layer ... why not??

Some (not all) of the bad decisions (like the u"..." strings) they did change
afterwards, but than it was a little late.

So many modules are still not ported to Python 3 -- so the hurdle is a little
to high -- for small to nil benefits!

So, the problem (from my side) is not Unicode at all ... just the lack of
reasonable support from the deciders side.

\---

Maybe, some time later, when I have to much spare time.

~~~
stillsut
Agreed. Also this:

"So expect Python 4 to not do anything more drastic than to remove maybe
deprecated modules from the standard library."

But _why_ break all the existing libraries that use those modules, even if
there's now "better" ways. In every comparison I've ever seen on performance,
robustness, etc python always loses to the other big languages. Except in one
area: the availability of user-land submitted packages and extensions. So why
break them for a little perf boost?

~~~
PythonicAlpha
Sounds really a bit weird to me.

I hope, that they really think hard, which modules really must be removed.

I would only consider security reasons to really remove a library module. Or
at least mark it deprecated for a long while until it is used only really
rarely.

------
henrik_w
This is a pretty good explanation of unicode in Python:
[http://nedbatchelder.com/text/unipain.html](http://nedbatchelder.com/text/unipain.html)

------
euske
I like Python3 personally. It's new and better but a _different branch_. I'm
annoyed by people abbreviating it as "Python" and treating it as a substitute
for Python2. In my opinion, the "Python" name should be exclusively used for
Python2, and Python3 should've been always used as one word. The whole Python3
situation caused unnecessary confusion to the outside (non-Python) people,
which I think could be avoided.

------
makecheck
Since I'm trying to keep a small footprint, I rely on the system version of
Python on Mac OS X, which is 2.7.10 now.

To use anything newer, I'd have to ask users to install a different
interpreter, or bundle a particular version that adds bloat. There's no point.
The most I've done is to import a few things from __future__; otherwise, my
interest in Python 3 begins when Apple installs it.

------
echlebek
The Go authors have solved this problem thoroughly. When working in Go, I
usually never have to think about this.

[https://blog.golang.org/strings](https://blog.golang.org/strings)

~~~
eugenekolo2
Go came out in 2009. What's your point? I'd sure hope they'd look at languages
older than them.

------
niels_olson
How long is the transition going to take? Serious question. Because I'm rather
tired of starting new work and finding some module that drags me back to 2.x.

~~~
ubernostrum
Technically, "forever", since there will be people who never port their code.
If you're depending on one of those holdouts, it's time to find a new
dependency, because if they haven't ported by now they won't and that's your
problem because...

in practical terms, the transition is about to be over, since now the Linux
distros are all-in on converting to Python 3 for their current or next
releases and that will forcibly move the unported libraries in the bin of
obsolescence.

------
onesixtythree
From the outside, Python 3 seems like a much better language. I don't have
strong views of its object system (I avoid OOP as much as I can) but it seems
like the string/bytes handling is much better, and I'm also a fan of map and
filter returning generators rather than being hard-coded to a list
implementation (stream fusion is a good thing). Also, I fail to see any value
in print being a special keyword instead of a regular function (as in 3).

What I don't get is: why has Python 3 adoption been so slow? Is it just
backward compatibility, or are there deeper problems with it that I'm not
aware of?

~~~
harryf
What makes me snarky is the replacing of

    
    
        '%s %s' % ('one', 'two')
    

With

    
    
        '%s %s'.format('one', 'two')
    

The latter is just more annoying to type. Stupid argument I know but I find
myself grumbling to myself every time...

~~~
mixmastamyk
Simplified to this in Py 3.6:

    
    
        f'{one} {two}'

~~~
teddyh
Do you have a reference for that? Wouldn’t that be introducing a huge security
hole in all programs?

~~~
mixmastamyk
PEP 498. Literals only, does not add any _additional_ security problems.

~~~
teddyh
Ah, right; I missed the ‘f’ prefix. And since it’s only done when parsing the
expression, it is not a security problem. Thanks!

------
mathgenius
Ok, fine. Can we have the print statement back?

~~~
untothebreach
Genuinely curious, why do you prefer `print` to be a statement rather than a
function? I've heard a lot of criticisms of Py3, but this is the first time
I've heard this one.

~~~
bufordsharkley
I think a function is "better", but the print statement should have been
preserved.

Print is used either for:

1) Writing to stdout/stderr 2) Debugging the hacky way

The print function is better for the former (though more often than not, I use
Armin Ronacher's click.echo for compability)[0], but I fastly prefer the print
statement for (2). Not dealing with parentheses is always a plus; I can add
and remove print statements far more quickly.

I find it to be an increase in friction, and I don't see any real downside in
leaving it in.

[0]
[http://click.pocoo.org/5/api/#click.echo](http://click.pocoo.org/5/api/#click.echo)

~~~
untothebreach
I agree with you on 1), I much prefer `print(..., file=sys.stderr)` to `print
>>sys.stderr, ...`.

For 2), though, I have always used auto-inserting parens in my vim, so I never
really experienced any pain w.r.t. parentheses. I can see how that would be a
pain, though.

------
gnrme
I think this is the primary reason why some scripting languages end up in the
education space (as a tool for learning), while others go mainstream and
ubiquitous in the commercial space. Breaking stuff between versions is a
headache and expense for everyone except the most superficial users.

The 'there should be one – and preferably only one – obvious way to do it'
rule sounds like another reason. It's like being asked to choose between a
perfect general use knife or a Swiss army knife.

~~~
gipp
This would _almost_ be a reasonable post if Python weren't mainstream and
ubiquitous in the commercial space.

