The Case Against Python 3 (For Now)

This document serves as a collection of reasons why beginners should avoid Python 3 as of November 22nd, 2016. I give two sets of reasons as I see them now. One for total beginners, and another for people who are more knowledgeable about programming. The first section attempts to explain the case against starting with Python 3 in non-technical terms so a beginner can make up their own mind without being influenced by propaganda or social pressure. The second section then explains the reasons in a series of direct challenges to the Python project of problems that need to be fixed before a beginner (and probably most programmers) can use Python 3 effectively.

I cannot teach Python 3 to total beginners because I don't want them to think they "suck at programming" when really it's Python 3's fault. That's simply not fair to them, so I have to teach Python 2 so they have the best chance at learning to code.

The Most Important Reason

Before getting into the main technical reasons I would like to discuss the one most important social reason for why you should not use Python 3 as a beginner:

There is a high probability that Python 3 is such a failure it will kill Python.

Python 3's adoption is really only at about 30% whenever there is an attempt to measure it. Nobody is all that interested in finding out what the real complete adoption is, despite there being fairly simple ways to gather metrics on the adoption. In my honest opinion the language is dead and will not advance far simply because of the technical reasons I list below. These reasons are simple usability problems that can be fixed easily, but the Python project has adopted an arrogant stance that these defects are in fact "features" that are good for you. This is a death sentence for the language, and if you're planning on using Python 3 in the future you will most likely be in trouble.

It's as simple as that. If you learn Python 2, then you can still work with all the legacy Python 2 code in existence until Python dies or you (hopefully) move on. But if you learn Python 3 then your future is very uncertain. You could really be learning a dead language and end up having to learn Python 2 anyway.

Also, it's been over a decade (it's been in development for a really long ass time, even though the release was 2008), maybe even multiple decades, and Python 3 still isn't above about 30% in adoption. Even among the sciences where Python 3 is touted as a "success" it's still only around 25-30% adoption. After that long it's time to admit defeat and come up with a new plan.

Reasons Beginners Can Grok

There are more reasons beyond just the uncertain future of Python 3 even decades later (released 2008, worked on for way longer). I'll attempt to explain the more technical reasons in a way a non-programmer can understand using analogies, but if the analogies annoy you then you can also read the more technical explanations below.

Not In Your Best Interests

The Python project's efforts to convince you to start with Python 3 are not in your best interest, but, rather, are only in the best interests of the Python project.

This should ultimately be the only reason you need to not start with Python 3. Ask yourself, why are they so keen on having you use a language that has only about 30% adoption, is constantly changing, and full of issues? The reason is getting beginners hooked on Python 3 means more adoption, which benefits Python 3. They don't care that you'll have problems, struggle too much, and potentially fail because of technical limitations in Python 3. They only care that you, a beginner, will amount to one more user. One more true believer.

I want you to just get started programming and that is all. I don't care what language you use, which is why I have a free book on Python and on Ruby. The language doesn't matter to me at all, and I'm not going to push a broken language on you. The Python project, however, has no problem pushing a broken language on you so that means you should avoid it until they fix it. Some people go so far as to ban my book because it does not support Python 3, even though my book is the most effective method for learning to code. The second someone starts banning books you know they don't care about anything but their own agendas.

Ultimately though, if Python 3 were good they wouldn't need to do any convincing to get you to use it. It would just naturally work for you and you wouldn't have any problems. Instead, there are serious issues with Python 3 for beginners, and rather than fix those issues the Python project uses propaganda, social pressure, and marketing to convince you to use it. In the world of technology using marketing and propaganda is immediately a sign that the technology is defective in some obvious way.

This use of social pressure and propaganda to convince you to use Python 3 despite its problems, in an attempt to benefit the Python project, is morally unconscionable to me.

Poor Design Decisions Prevent Adoption

The Python project decided to make it impossible to run legacy Python 2 code under Python 3. They even go so far as to claim this is "impossible" when basic computer science says this is very possible and done all the time. Given they controlled both language implementations and purposefully decided to prevent Python 3 from running legacy code, I have to conclude that Python 3 is defective on purpose. The end result of this defect is that most people will not bother switching to Python 3 as rewriting Python 2 code is the same switching costs as just using a totally different language completely.

This means you are working with a purposefully broken platform when you use Python 3, and I personally can't condone teaching people to use something that is fundamentally broken.

Difficult To Use Strings

The strings in Python 3 are very difficult to use for beginners. In an attempt to make their strings more "international" they turned them into difficult to use types with poor error messages. Every time you attempt to deal with characters in your programs you'll have to understand the difference between byte sequences and Unicode strings. Don't know what that is? Exactly. The Python project took a language that is very forgiving to beginners and mostly "just works" and implemented strings that require you to constantly know what type of string they are. Worst of all, when you get an error with strings (which is very often) you get an error message that doesn't tell you what variable names you need to fix.

In addition to that you will have 3 different formatting options in Python 3.6. That means you'll have to learn to read and use multiple ways to format strings that are all very different. Not even I, an experienced professional programmer, can easily figure out these new formatting systems or keep up with their changing features.

Throw into that a proposal for yet another string type and you have a nightmare for anyone who's starting out in programming. The reason why I loved Python for beginners is it was easy to use. A total beginner could easily start making useful code right away. The Python 3 project has ruined that in their failed attempt to internationalize Python.

When you start out programming the first thing you work with is strings, and python made them far too difficult to use for even an experienced programmer like me to use. I mean, if I struggle to use Python's strings then you don't have a chance.

Core Libraries Not Updated

Many of the core libraries included with Python 3 have been rewritten to use Python 3, but have not been updated to use its features. How could they given Python 3's constant changing status and new features? In my own testing I've found that when a library could detect Unicode it fails to do so and returns raw byte arrays. What that means is you'll use a library, get what you think is a text string, and then have your code explode with a weird error message that has no variable names in it. This then is randomly distributed, with some libraries figuring out the Unicode status of data and others failing, and even some that fail or succeed within the same library.

Until there is a standard for what every library should return you cannot rely on Python 3's libraries to properly give you characters encoded. The solution is to create a "string" type that magically knows how to be Unicode or raw bytes, but the Python community is against anything they consider "magic" so they'd rather beat you over the head with their bad design and tell you it's a nice massage.

Reasons For The Programmers

Obviously you're then going to show this document to your seemingly knowledgeable friends, or maybe even big high profile people within the Python project. I have written this section for them so they can see, using code samples, exactly why Python 3 sucks for you. You may not understand this currently, but your friend will and hopefully it will make them go away so you can get back to just learning to code.

Before we begin, remember that I have already converted a good portion of Learn Python The Hard Way to Python 3 so any claims that I will not support it are unfounded. I will support it, when it works for beginners. Until then, it's a violation of my ethics to force beginners to use something that is not easy for them to use.

The Python 3 VM Is Poorly Designed

The choice to not have Python 3 run legacy code will be the main reason it does not get adopted. Here's the breakdown of my reasoning:

  • Python 3 uses a Virtual Machine (VM) just like Python 2 did.
  • The original design of Python 3 could have also run Python 2 in this same VM. They controlled both languages, and could make it work. They chose not to do this, and instead tell you to manually translate.
  • F#/C# and JRuby/Java are good examples of doing this. The former, when you have control over both languages. The latter, when you don't.
  • Successive versions of Java are another example of doing this, and may be closer to Python 2/3 integration.
  • Choosing not to support both Python 2 and Python 3 in the same VM makes the transition cost off Python 2 much higher than it needs to be. Rather than support a smooth transition, the Python project instead asks you to manually convert code instead of simply supporting both languages.
  • When asked why they don't run both, members of the Python project have actually told me this is impossible. This is a lie. It is not impossible, and in fact would have been the better design to help with migration.
  • This poor design choice of Python 3 prevents easy migration, reduces adoption, and forces you to do their work for them. The difficulty in having to manually translate code simply because Python 3 refuses to support Python 2 by design means that there is a high probability it will not succeed.
  • When I say "it will not succeed" people think, "compared to Python 2", when I really mean, compared to other languages.
  • To me, I find it unconscionable to use other people's free labor to cover for a poor design decision. I doubt they'll fix this, but it's my evidence that says Python 3 will not see the necessary adoption to be successful.

Note

In the previous version I trolled people by pointing out that, if what the Python project says is true and it would have been "impossible" to support Python 2, then they broke it and Python 3 is not turing complete. Obviously Python 3 is turing complete, but Python project members frequently claim something this basic is "impossible" soooooooooooo alright. I even had a note after the gag saying it was a gag, but everyone is too stupid to read that note even when they do elaborate responses to my writing. Even more telling was when people said this was stupid, I'd feign ignorance further and ask, "Wait, so why doesn't Python 3 support Python 2 then?" This then sent them down a logic loop death spiral of simultaneously trying to defend the design decision and also state that Python 3 is fully capable. It was pretty funny to watch, but after a while I guess I have to straighten this out and simplify it so here you go.

Purposefully Crippled 2to3 Translator

I originally said this was 100% possible but honestly I should have said the 2to3 translator should have just not been necessary. I also think I was wrong in saying it was 100% possible. I think a better wording is, "If they designed Python 2 and 3 correctly the 2to3 translator would work perfectly."

I also feel this section just doesn't help explain why Python 3 isn't usable for beginners. The high switching cost of having to manually rewrite code is a valid concern. The lack of support for running Python 2 code is a valid friction on switching. Having a broken 2to3 translator though is just a symptom of the problem, not a cause.

This version will have this not but eventually I'll strip this down to the essentials and remove this pointless point I tried to make.

Statically Typed Strings

Python is a dynamically typed language. That means I do not have to know the type of variable to use it. As long as it behaves like another type, I am free to effectively pretend it is that other type. Whether that's a good computer science decision or not is pointless. Dynamic typing is what makes Python easy to use and one of the reasons I advocate it for beginners.

In Python 3, I cannot reliably use these function:

def addstring(a, b):
    return a + b

def catstring(a, b):
    return "{}{}".format(a, b)

If we have a string and a bytes type then when we call the first function we get an error, and the second we get a repr formatted byte string. I actually prefer the first response as it means I'm not randomly sprinkling b'blah' all over the place without knowing it.

Here's me trying to use these two functions in Python 3.5:

Python 3.5.1 (default, Sep 16 2016, 13:36:12)
[GCC 4.2.1 Compatible Apple LLVM 7.3.0 (clang-703.0.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> def catstring(a, b):
...     return "{} {}".format(a,b)
...
>>> x = bytes("hello", 'utf-8')
>>> y = "hello"
>>> catstring(x, y)
"b'hello' hello"
>>>
>>> def addstring(a, b):
...     return a + b
...
>>> addstring(x, y)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in addstring
TypeError: can't concat bytes to str
>>> ^D

By comparison here's the Python 2 version:

Python 2.7.11 (default, May 25 2016, 05:27:56)
[GCC 4.2.1 Compatible Apple LLVM 7.3.0 (clang-703.0.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> def catstring(a, b):
...     return "{}{}".format(a,b)
...
>>> def addstring(a, b):
...     return a + b
...
>>> x = "hello"
>>> y = bytes("hello")
>>> catstring(x, y)
'hellohello'
>>> addstring(x, y)
'hellohello'
>>>

There is no technical reason for the Python 3 version to differ from the behavior of the Python 2 version, they just decided that you should have to handle all the type problems related to Unicode and bytes with no help. This a classic "Macho Code Guy" move and it has no place in a language for beginners. Difficult usability is not a marker of a better programming language, or better programmers. It is only a marker of arrogant people doing the implementation. However, when you point out that this is hard to use they try to claim it's good for you. It is not. It's simple blustering covering for a poor implementation.

A final note on this is even the error message is macho and terse:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in addstring
TypeError: can't concat bytes to str

If they're going to require beginners to struggle with the difference between bytes and Unicode the least they could do is tell people what variables are bytes and what variables are strings. They probably consider helpful error messages "magic" though so don't hold your breath they'll actually fix this one.

Dynamic and Static Mismatch

One fatal flaw of this decision to "static type" the strings is Python lacks the type safety gear to deal with it. Python is a dynamic language, and doesn't support type declarations on function arguments. It's also not statically compiled as strongly as it could be, so you can't find these kinds of type errors until you run the code. In the past this wasn't an issue because you'd write tests and the dynamic nature of Python meant things kept working as long as the method signature of types was the same.

Strings are also most frequently received from an external source, such as a network socket, file, or similar input. This means that Python 3's statically typed strings and lack of static type safety will cause Python 3 applications to crash more often and have more security problems when compared with Python 2.

Core Libraries Not Updated

There are several libraries that, despite knowing the encoding of data, fail to return proper strings. The worst offender seems to be any libraries dealing with the HTTP protocol, which does indicate the encoding of the underlying byte stream in many cases. In many cases the library uses the old API that relied on dynamically typed strings, but doesn't update to help with the returned values. It returns what you think is a "string" but is really bytes when the proper return should now most likely be the raw bytes plus any encoding information the library can figure out.

What's even more idiotic about this is, Python has a really good Chardet library for detecting the encoding of byte streams. If Python 3 is supposed to be "batteries included" then fast Chardet should be baked into the core of Python 3's strings making it cake to translate strings to bytes even if you don't know the underlying encoding. In fact, I'd like to see this:

x = bytes('hello', 'utf-8')
y = x.guess()

The point being that character encoding detection and negotiation is a solved problem. Other languages do it to varying degrees of success without the constant exceptions being thrown. Python 3 however has taken the stance that, since they think strings being unicode are "important", that means you must constantly be aware of them and constantly have to deal with them. To them, usability is counter to safety, when that's not true at all.

Too Many Formatting Options

Currently Python 3 supports the old style of formatting:

print "Howdy %s" % ('Zed')

The new .format() style:

print "Howdy {}".format('Zed')

And in Python 3.6 we will have a formatting style that's more like Ruby's:

x = 'Zed'
print f"Howdy {x}"

I really like this new style, and I have no idea why this wasn't the formatting for Python 3 instead of that stupid .format function. String interpolation is natural for most people and easy to explain.

The problem is that beginner will now how to know all three of these formatting styles, and that's too many. When I update Learn Python The Hard Way to Python 3 I'm only going to teach the 3.6 "f-string" style, and then mention the other two only as legacy code.

Even More Versions of Strings

Finally, I'm told there is a new proposal for a string type that is both bytes and Unicode at the same time? That'd be fantastic if this new type brings back the dynamic typing that makes Python easy, but I'm betting it will end up being yet another static type to learn. For that reason I also think beginners should avoid Python 3 until this new "chimera string" is implemented and works reliably in a dynamic way. Until then, you will just be dealing with difficult strings that are statically typed in a dynamically typed language.

Conclusions and Warnings

I have resisted writing about these problems with Python 3 for 5 versions because I hoped it would become usable for beginners. Each year I would attempt to convert some of my code and write a couple small tests with Python 3 and simply fail. If I couldn't use Python 3 reliably then there's no way a total beginner could manage it. So each year I'd attempt it, and fail, and wait until they fix it. I really liked Python and hoped the Python project would drop their stupid stances on usability.

After 5 versions I'm ready to admit that Python 3 is simply broken for beginners and move on. I'm hoping that writing this list will prompt some kind of change, but I doubt it. As the versions of Python 3 progressed--and the adoption was lacking--the Python project started using social pressure, propaganda, and marketing to gain traction instead of fixing Python 3's obvious usability problems. This is a dangerous position because it is easy to fix problems that are seen as simple technical or usability flaws. It is very difficult to fix problems that are erroneously viewed as positive social goods.

Currently, the state of strings is viewed as a Good Thing in the Python community. The fact that you can't run Python 2 inside Python 3 is seen as a weird kind of tough love. The brainwashing goes so far as to outright deny the mathematics behind language translation and compilation in an attempt to motivate the Python community to brute force convert all Python 2 code.

The brutal truth is if Python 3 had been designed to run Python 2 and 3 code together, and strings were as dynamic as the Python 2 strings, we would not be in this situation. Now, I fear that everyone who currently codes Python 2 is simply going to move to a more stable language like Go, Rust, Clojure, or Elixir. The switching costs of Python 3 are so high programmers will just switch to another language that isn't broken or is more exciting.

Which is probably why the Python project focuses on convincing unsuspecting beginners to use Python 3. They don't have a switching cost, so if you get them to fumble their way through the Python 3 usability problems then you have new converts who don't know any better. To me this is morally wrong and is simply preying on people to prop up a project that needs a full reset to survive. It means beginners will fail at learning to code not because of their own abilities, but because of Python 3's difficulty.

It's sad to watch Python destroy itself because it's such a great language, but that seems to be where things are headed. It's my hope that this essay wakes them up and they change their path to something much more friendly to everyone. That they admit Python 3's design was seriously flawed and start Python 4 in an attempt to rescue the language. Sadly, I doubt this will happen, and instead they'll just rant about how I don't know what I'm talking about and I should shut up. My book is already banned on many Reddit subreddits, and various high profile Python project members work fairly hard to censor it because of my stance on their strategy. After this essay they will probably double down on their stubbornness and refuse to listen.

No matter what happens though, I'll still continue to teach total beginners in the best way I can so they have the easiest path to code competency and a long love of programming.

Thank you for reading this.

Buy Paper, PDF, or ePub (I Make 12%)

If you prefer Paper, PDF, or ePub then I recommend you buy it from my publisher at InformIT.com (run by Pearson). You can also sign up for the Free Support Course to receive a few key videos, get chat help, and errata for the book.

Buy Paper, PDF, or ePub from InformIT

Buy Learn Python the Hard Way, 5th Edition from Me (I make 100%)

When you buy Learn Python the Hard Way, 5th Edition directly from the author, Zed A. Shaw, you'll get access to the digital content for all 60 exercises, plus all 60+ videos that are normally sold separately. You'll also have access to direct help from Zed via Discord chat and live streams on special topics when I have time.

$29

Buy Learn Python The Hard Way, 5th Edition