Stop Using Python 2: What You Need To Know About Python 3

Though Python 3 was released in 2008, many projects are still stuck on Python 2.

It’s understandable that porting large existing codebases to a new version is a prospect which sends a shiver down many a developer’s spine. But code inevitably needs to be maintained, and so when all the shiny new features that would fix everything are in a new version, is it really worth staying rooted in the past?

We’ll take you through some of the features that Python 2 programs are missing out on, not only from 3.0 but up to the current release (3.7).

Why Python 3 Happened

Before 2008, Python developers had a bit of a headache. The language that started in the 1989 Christmas holidays as the pet project of Guido van Rossum was now growing at a fast pace. Features had been piled on, and the project was now large enough that earlier design decisions were hindering implementation. Because of this, the process of adding new features was becoming an exercise in hacking around the existing code.

The solution was Python 3: the only release that deliberately broke backwards compatibility. At the time, the decision was controversial. Was it acceptable for a publicly used open source project to purposefully break on older code? Despite the backlash, the decision was taken, giving Guido and the developers a one off chance to clean out redundant code, fix common pitfalls and re-architect the language. The aim was that within Python 3 there would be only one obvious way of doing things. It’s testament to the design choices made back then that we’re still on 3.x releases a decade later.

The __future__ is Now

The __future__ import is a slice of time-travelling wizardry which allows you to summon select features from future releases of Python. In fact, the current Python release, 3.7, contains __future__ imports from releases which haven’t yet been written!

Ok fine, so it’s not quite as grandiose as that, a __future__ import is just an explicit indicator of switching on new syntax which is packaged with the current release. We thought we’d mention it because a few of the Python 3 features listed below can be __future__ imported and used in 2.6 and 2.7, which were released to coincide with 3.0 and 3.1 respectively. Having said this, upgrading is, of course, still advised as new features are ‘frozen’ in past releases and will not benefit from the evolution and maintenance of current versions.

Onto what you’re missing out on in Python 3…

Print is a Function

Yes, we know that most people are aware of this, but it’s one of the most used statements by Pythonistas who are starting out. print moved from a keyword to a function, which just makes sense.

This Python 2 code

print "Fresh Hacks Every Day"
print "Foo", "some more text on the same line as foo"

will become the following in Python 3.

print("Fresh Hacks Every Day")
print("Foo", end='')
print("some more text on the same line as foo")

Souped up Unpacking

Here we have a tuple containing some data. In Python 2, we can already unpack into different variables like so:

person = ("Steve", "Hammond", 34, "England", "spaces")
name, surname, age, country, indent_pref = person

But let’s say we only care about the first name and indentation preference. In Python 3, we can now unpack like this:

person = ("Steve", "Hammond", 34, "England", "spaces")
name, *misc, indent_pref = person

# These would also work
name, surname, age, *misc = person
*misc, indent_pref = person

This provides much more flexibility when unpacking — especially handy if dealing with tuples longer than the one in this example.

Unpacking is commonly used in for-loops, especially when using things like zip() or enumerate(). As an example of applying the new syntax, we now have a function, get_people_data(), that returns a list of tuples like the person example above.

for name, *misc, indent_pref in get_people_data():
    if indent_pref is not "spaces":
        print(f"You will feel the full wrath of PEP 8, {name}.")

This works great. But wouldn’t it be nice if we could store the indentation preference in a better way than just a string? In a way similar to an enum in C?

Enums

Enums in Python. What a treat. Due to popular demand, they’ve been in the standard library since 3.4.

Now we can define indentation preference like this:

from enum import Enum

class Indent(Enum):
    SPACES = 'spaces'
    TABS = 'tabs'
    EITHER = 'either'

person = ("Steve", "Hammond", 34, "England", Indent.SPACES)
# or
person = ("Steve", "Hammond", 34, "England", Indent("spaces"))

# Let's try and initialise with an invalid value
person = ("Steve", "Hammond", 34, "England", Indent("invalid value"))
# 'ValueError: 'invalid value' is not a valid Indent

The syntax seems a bit strange, but it can be useful when dealing with numeric constants or initialisation from strings.

Division

A simple but major change: when dividing integers in Python 3 we get true float division by default (dividing two integers in Python 2 always resulted in an integer result).

This is the Python 2 behaviour:

>>> 1 / 3
0
>>> 5 / 2
2

Whereas in Python 3 we get more accuracy:

>>> 1 / 3
0.3333333333333333
>>> 5 / 2
2.5

// can of course be used for floor integer division if this is required.

This change is one that should definitely be noted if you’re porting code from 2 to 3; it’s an easy one to slip under the radar that could cause major issues in program logic.

Chaining Exceptions

It’s very common to catch one exception, then raise another. This could be because your application has a defined set of custom exceptions, or simply because you want to provide more information about what went wrong.

Here we have a program which crudely calculates the number of days needed to be worked to earn a certain proportion of yearly pay.

class InvalidSalaryError(Exception):
    pass

def days_to_earn(annual_pay, amount):
    """Return number of days worked to earn `amount`."""
    try:
        annual_frac = amount / annual_pay
    except ZeroDivisionError:
        raise InvalidSalaryError("Could not calculate number of days")
    return 365 * annual_frac

if __name__ == '__main__':
    print(days_to_earn(0, 4500))
    print(days_to_earn(20000, 4500))

We can see that if an annual pay of zero is specified and a ZeroDivisionError occurs, this is caught, and an InvalidSalaryError is raised.

Let’s try running this with Python 2.

$ python days_calc.py
Traceback (most recent call last):
  File "exception_chaining.py", line 13, in <module>
     print(days_to_earn(0, 4500))
  File "exception_chaining.py", line 9, in days_to_earn
    raise InvalidSalaryError("Could not calculate number of days")
__main__.InvalidSalaryError: Could not calculate number of days

Because we caught the ZeroDivisionError, it got swallowed, so only the InvalidSalaryError traceback is shown.

Now let’s run this with Python 3.

$ python3 days_calc.py
Traceback (most recent call last):
  File "exceptions_chaining.py", line 7, in days_to_earn
    annual_frac = amount / annual_pay
ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "exceptions_chaining.py", line 13, in <module>
    print(days_to_earn(0, 4_500))
  File "exceptions_chaining.py", line 9, in days_to_earn
    raise InvalidSalaryError("Could not calculate number of days")
__main__.InvalidSalaryError: Could not calculate number of days

We got a far more detailed error report, explaining what caused the InvalidSalaryError, complete with a traceback for the ZeroDivisionError. This is particularly useful when you’re using code written by someone else, which might not have the most verbose error messages.

We won’t go into it here, but there’s also new raise ... from syntax which allows for explicit exception chaining.

Side note: dealing with large numbers in Python

In the above program we had to deal with ints in the tens of thousands. Writing long numbers like 20000 or 0b001110100010 is a recipe for screen squinting. How about separating them like this: 20_000, 0b_0011_1010_0010? This is another way Python 3 will make your life easier – optional underscores in numeric literals, which can help to make things more readable.

Unicode and Bytes

In Python 2 the concept of bytes and strings were pretty much interchangeable, and both came under the str type. This led to some very nasty conversion consequences and unpredictable behaviour. The headline to remember is that in Python 3 all strings are unicode. A distinction was very deliberately created between text and bytes, offering a much more defined way of working.

In the below examples, we test the type of a bytestring, ‘normal’ string and unicode string in the interpreters of Python 2 and 3.

Python 2.7:

>>> type(b'foo')
<type 'str'> # In Python 2, a bytestring is a normal string!
>>> type('foo')
<type 'str'> # The same bytes type as above
>>> type(u'foo')
<type 'unicode'>

Python 3.7:

>>> type(b'foo')
<class 'bytes'> # In Python 3, this has its own bytes type
>>> type('foo')
<class 'str'> # In Python 3, 'str' means unicode
>>> type(u'foo')
<class 'str'> # It's a normal string

This means that dealing with encodings is much clearer in Python 3, even if it comes at the cost of slipping in a few more .encode()s. Chances are that when your program communicates with anything in the outside world, such as files or sockets, you’ll have to encode and decode your data.

As an example, if you’re using pyserial to read/write to a serial device, you’ll need to explicitly encode and decode your messages.

import serial

PORT = '/dev/ttyACM0'
BAUD = 115200
ENCODING = 'utf-8'

if __name__ == '__main__':
    ser = serial.Serial(port=PORT, baudrate=BAUD)

    ser.write('hello'.encode(ENCODING))
    response_bytes = ser.readline()
    response_str = response_bytes.decode(ENCODING)

String Formatting

Formatting strings in Python 2 was performed in a similar style to C:

author = "Guido van Rossum"
date = 1989
foo = "%s started Python in %d" % (author, date)

# Guido van Rossum started Python in 1989

The docs explicitly state that this way of formatting “exhibits a variety of quirks that lead to a number of common errors (such as failing to display tuples and dictionaries correctly)”. It’s also inflexible, and starts to become ugly to look at when dealing with long strings.

We now have two new ways of formatting strings. One is ultra-convenient, and one is ultra-powerful.

Formatted String Literals

Often referred to as ‘f-strings’, these provide an incredibly easy way to reference any variable available in the current scope.

Checkout how intuitive and readable this code is:

author = "Guido van Rossum"
date = 1989
foo = f"{author} started Python in {date}"

# Guido van Rossum started Python in 1989

Anything inside the curly braces will be evaluated as an expression, so you can put small logic or statements inside the string if you so desire. It’s probably not very readable to contain program logic within a string, but if it’s just getting logged to provide extra debug information then it’s really handy.

author = "Guido van Rossum"
date = 1989 
foo = f"{author.split()[0]} started Python {2000 - date} years before the turn of the century"

# Guido started Python 11 years before the turn of the century

The .format Method

In Python 3, every string has a .format method, which provides a dazzling array of options for formatting, covering the overwhelming majority of use cases. We won’t go into detail here as it is backported to 2.6 and 2.7, so won’t be a big upgrade pull for most Python 2 users.

Here’s one of our favourite guides for reference.

Imports

Let’s say that you’re using Python 2, and have the following file hierarchy:

pkg
├──service.py
└──misc.py
run.py
utils.py

run.py simply imports and calls something from the service module in the pkg package.

But service.py relies on functions contained in utils.py. So at the top of service.py we have this statement.

import utils

Seems fairly unassuming, and everything will work just fine. But what if our folder structure now changes, with pkg acquiring a new utils module?

pkg
├──service.py
├──misc.py
└──utils.py
run.py
utils.py

Time for confusion: our code now switches and uses the utils.py file within pkg. Things would get even more messy if we happened to have a library installed named utils. This approach isn’t defined enough, and consistently led to unpredictable behaviour in Python 2.

Python 3 to the rescue: it’s no longer supported syntax if it’s ambiguous whether the import is supposed to be absolute or relative. The top of service.py could become any of the following options depending on what’s required.

# To import from within pkg, either use relative
from . import utils
# or absolute
from pkg import utils

# To import from the same level as run.py, use absolute
import utils

This feature might seem like a bit of a nicety in this example, but when dealing with big codebases containing large package/import hierarchies you’ll be glad of it.

Other

There are many, many other features which offer improvements and entirely new features over Python 2, even if some are only useful in niche areas. Here are just a few:

  • The addition of the asyncio library makes writing asynchronous programs a breeze. A must in modern programming.
  • Other major standard library additions (like concurrent.futures, ipaddress, and pathlib)
  • Accessing a parent class in Python 2 is needlessly heavy on syntax. In Python 3, super() becomes even more magical.
  • Many builtins such as zip(), map(), and filter() now return iterators instead of lists. In many cases, this will save significant amounts of memory without you even knowing.

Tools

If you decide that porting code from Python 2 to 3 is the way to go, there are some existing tools to make your life easier.

  • 2to3 – the official automated code translation tool. Yes, it will port the code for you! Not guaranteed to catch everything, but does a lot of the tedious syntactic fiddling.
  • caniusepython3 – a nifty utility for detecting which of your project dependencies are stopping you from making the leap.
  • tox automates and streamlines the testing of Python code. It allows you to easily test your code on multiple versions of Python, which is fantastic for any project in general, but particularly comes in handy when you’re testing the success of your newly ported codebase on different versions.

Conclusion

There are countless reasons to upgrade to Python 3 – not just because of the convenience of new features, but because random, obscure bugs in the language get fixed regularly by Python developers, in Python 3 alone. Only the most robustly tested, never-need-to-change codebases have an excuse to remain at Python 2. Everything else should be enjoying the fantastic hard work put in by Python developers to make the language what it is today.

113 thoughts on “Stop Using Python 2: What You Need To Know About Python 3

      1. Yes, and also note that this is Google. The same Google that once employed the creator of Python and uses Python heavily in e.g. their AI stuff (TensorFlow, etc.) Pretty embarrassing for their Python guide to state they recommend avoiding Python 3.

    1. Hi from the Google Python Team: Thanks for pointing this out! I’m looking into getting this updated. Nobody should avoid Python 3 these days!

      Note that this Python course is really from 2010 (see the associated videos) back when Python 2.7 was brand new and Python 3 was still being polished up and not well supported by anyone.

  1. The whole Python 2 vs 3 crap really made me uninterested in programming python, sure I used and still use lots of python dependent FOSS but I never made it “one of my languages” so to speak. Since the advent of Julia and Jupyter notebooks I’ve gotten much more interested and the fact they are working to kill off Python 2 has got me even more excited, finally its ready for prime time.

      1. I’d highly recommend using 3.6 or higher, there were some quite nice performance improvements that came with 3.6, so much so that many 2.7 diehards finally gave 3.6 the nod as ‘OK, I can’t justify not switching now’.

      1. It’s actually great. Because it forces people to write more readable code.

        And again my mantra: use a bloody IDE,
        it’s the same with all those morons complaining about how annoying C can be if you forget a ; somewhere…

      2. The whitespace rules are simple, so everyone knows its a tab and not 5 spaces for example. I think using whitespace instead braces makes code easier to read and more consistent between coders.

      3. If you don’t already program with significant whitespace, your code is a nightmare and no one wants to read it. Otherwise, requiring consistent indentation (the only requirement Python actually has) is not something to be afraid of.

  2. I am not a python programmer (despite taking some quick online course on it); I found it to “deliberately break code from previous versions” a very bold move (for any language actually). But if it is for a good reason (to clean future code), I am all for it

    1. Perhaps if the developers had focused on all of the distribution problems that plague python, instead of hypocritically nit-picking things like the usage of the print function, while simultaneously giving the raise function a pass, then people would have adopted it earlier.

      The bottom line is that the developers went off on a tangent for a decade and now conversion of the population can only be done by evangelism, because the new features aren’t compelling enough to stand on their own merits.

  3. I kinda agree with some points, and disagree with others. The division thing, though, was wrong from the beginning in the old versions ( any good language has float and integer divisions , so making something that would be thought as float divisioni return only integers was a bad move ) .

    But the whole thing of breaking compatibility made me lose interest in the language. Wanting to run some little utility from the ´net and having to discover which “python version of the day” it needed and trying to install it brings no joy. Then later we don´t even know which version is running for default. They could have kept compatibility with old code in the newer versions, they just didn´t want to.

    1. “Python version of the day” is a bit hyperbolic, considering Python has only broken compatibility once, over a decade ago now. I guess the issue is coming up again because several Linux distros are just now making Python 3 their default Python.

  4. I have used Python in the past and despite some nice features, I really cannot like a language that uses white space for block structuring, No doubt everyone will say use the IDE but the embedded system I was using it on didn’t have an IDE and insisted on compiling the code whenever you updated a source file, so you were screwed if your editor happened to use a different tabbing strategy to any code you were modifying (later versions did allow download of precompiled modules, which was an improvement since you could use an IDE on a PC or somesuch, but early development was fraught with danger). Of course. swapping a single tab for a space may no longer break the block structuring… I stopped using the language as soon as I didn’t have to work on that system any more.

    1. No kidding! And along with 2.7 vs 3.0 issues that made WIndows DLL hell look positively pleasant, cryptic error reporting, playing “guess which way to install additional modules”, and being one of the single worst languages for performance, I’ll go out of my way to avoid ANYTHING to do with Python.

      People thought Perl was painful, then along came Python to show them what pain truly was.

          1. ! Most of these newer, lesser languages are simply Perl without some of the interesting “features”.
            Once you know a few, this is obvious. I’m not fond of obscure regular expressions, but after Larry put them in,
            all languages have them now. Who is leading the way, again?

            In the case of python, they should just have called it a new name instead of the same one and breaking compatibility.
            They could also have adopted “there’s more than one way to do it” and not pissed off everyone who hates
            whitespace instead of brackets (which are far easier to bounce back and forth on in most editors).
            Perl was making the same mistake, but I think perl 6 is pretty much known as Rakudo now, since it’s really not the same at all.

            I find perl gives you enough rope to either tie up the mule and get a load hauled, or to shoot yourself in the foot.
            I don’t blame the language for crappy self-impressed too-clever coders thinking obfuscated meaning is job security – you can do that in any language. Is there even one that DOESN’T have an obfuscated or golf contest?
            Those kinda people should write bad poetry and die in obscurity.

            Having had to at least borrow a lot of python lately (like device drivers for raspberry pi stuff) – perl’s Inline::Python compiles the python – and reveals tons of horrible python coding, particularly in drivers where it’s so easy to sleep n milliseconds, the writer never learned to check a ready bit. And you discover this in perl because, believe it or not, python runs faster inside perl! Personally, I like easier/better control of namespace pollution than you get in python.
            But that’s a preference, not a life-death thing. I kinda like it that I have to specifically include something having to do with high precision timing right up front to warn readers that this kind of trickery is going on…

            To repeat – it’s not the language. It’s the coders. The more accessible you make the language, the more dumb beginners and generally bad artisans jump on board. You’re not going to fix that anytime soon.
            Every language I’ve seen in a long time watching that was going to “fix bad coding practice or make it impossible” has turnd out to have near-universal disdain in the end. Wonder why.

          2. I mean, I think there’s something to be said for the sharp distinction between Perl’s “many ways to do it” and Python’s “one true way to do it”… most of the advantages / disadvantages fall out because of that split. Perl is (maybe) hard to maintain because everyone has their own style that still works and gets the job done. But it’s harder to hack something out quickly, and the resulting Python is (maybe) inelegant because the language forced you to fit your program to the designer’s whims, instead of the other way around. And so on.

            The ecosystems are roughly on par IMO – CPAN is great, and I assume whatever Python uses is too (pip?) although the 2/3 split is the biggest headache there. “Batteries Included” is an interesting philosophy… but CPAN makes the batteries immediately at-hand so it’s not that big of a selling point. I do wish Local CPAN was more of a frontline support use case, a lot of people seem to balk at it when going to shared hosting and they can’t get their host to install some critical package.

            End of the day, I learned Perl first so I’m familiar with it. I would hate to give up regex as a first-class language feature. And both languages are preferable to PHP and JavaScript :P

        1. C is one of the best languages for performance. You can easily write your own python libraries in C. If you don’t already know how to do this, you’re missing out on one of the best parts of the language.

          1. Oops. didn’t mean to report the next post (tim), darn lack of editing here – hit the wrong link.
            But yes, the ability to use one language inside another, or to use one to write another is kinda cool and can be super useful and time saving. Sometimes I have a big bunch of number shoveling to do == C is the choice. But I need to also interface with humans, text, databases, stuff like that – almost anything else saves programmer time. So more than one language is good, at least for the complex stuff I do.

      1. What? Python is an incredibly performant language off the bat which is why it is the go to language for scientific work. Most people making such a claim have not the slightest idea what they’re talking about; it’s not just the performance potential that counts but the development efficiency and performance of built-in data structures. With Python you’re often at the “make it fast” stage before traditional languages even reach the “make it work” phase…

      1. That’s what I fell foul of – my editor used spaces (because I set it to) and nearly everyone else thinks tabs are the way it should be done. I wouldn’t have minded if they could all agree on the amount of indentation, but some of the C code I had to maintain fired half the line up against the right side of the edit window :-)

        1. The editor I used regarded the TAB key as an indent command and inserted virtual spaces that became concrete as soon as you typed something, so the one key to add isn’t really an issue. Once concrete it could delete white space up to the next character with a single key (mine was set to use shift/Del), so that wasn’t an issue either.

          As I said originally, TAB is fine if everyone agrees on the indentation, otherwise it becomes a mess once several people with different ideas about indentation work on code.

          However you cut it, using white space to define blocks is only going to work if you accept any white space and don’t terminate a block if someone inserts a line with different leading white space to that already in the block. Otherwise the only way to tell the block has terminated is to make white space visible so you can see the difference between the spaces and any TABS.

          1. Just for reference (yes, I’m going to go there– engage flame-war shields), there are macros for emacs to do the space/tab conversion seamlessly and invisibly, pretty much any way you prefer, and have been since the 1980’s when decent location-addressable displays finally displaces teletypes, DecWriters, and punchcards. As I have always been between systems (thankfully, IBM 360/370 line is no longer in the list… If I never have to write another JCL deck, I will be quite content. If I never have to use EBCDIC again, I may achieve true inner joy) I have avoided tabs with a passion since maybe 1980, since tabs don’t move portability and mess up formatting between devices (sometimes within a device).

            Before someone starts gabbling about non-fixed typefaces, note that all programming environs used fixed by default (some don’t allow non-fixed), and the typefaces for code display in most systems that are content aware are fixed.

            If you like to code with tabs, more power to you, but I tend to find (as do many others) that two or three indents with tabs leave the option of exceptionally wide screen real estate or accepting that you will lose some on one side or the other, as well as deal with side-scrolling window. “Lines shouldn’t be that long” you say? “you should be breaking up into smaller functions if they are” you say? Um… no. Really. Try writing ANYTHING meaningful in Java without at least 5 indent levels (presuming you follow the Java style prescriptions). Using common 8-space indent, that’s 40 characters. This entry window is about 96 char wide (content dependent, but I just counted several lines), so 5 indents is almost half, and 6 is. Yes, some editors let you set the tab columns. This makes it worse when working with other people or moving between systems.

            Yes, it is all about your preference, and I will work with tabs when I must, but prefer not. I can give a number of reasons tabs are preferable, as well, but, overall, IMHO, spaces rule and tabs drool\H\H\H\H\H\H\H\H\H\H\H\H\H\H\Hare preferable to tabs in virtually all cases.

          2. What you say is correct, but incomplete. Not using whitespace to define blocks immediately imports treating impossible to read code as legitimate. While conversion of everything from tabs into spaces invisibly and automatically is something any editor can do, converting nightmarish brace-based formatting into well-formatted code is something no editor can do reliably, especially without breaking the future ability for sensible diff-ing.

            And it imports the need for mentally opening/closing blocks while reading through the code as nothing but balancing the braces mentally can reliably account for them, a task unnecessary in code that mandates consistent indentation. Almost all sensible codebases would work perfectly fine if you stripped the braces and treated the indentation as indicative of block membership. Many large teams/codebases mandate exactly this by creating git checkin triggers that convert tabs to spaces and apply formatters or require such things before checkin can proceed. It’s just a matter of the braces being required too, at which point they’re really just superfluous.

      2. No. Tabs are the RIGHT way to do things. The only reason tabs so often end up being a problem is because some jerks still insist on mixing in spaces or they set their editor to display tabs as spaces and then proceed to fill the code with multiple tabs per level of indentation.

        Just use ONLY tabs for indentation and ONLY ONE per level of indentation. Then customize your editor’s settings to make it look good to YOUR eyes. Done! Now don’t mess it up!

        Here’s the thing.. we each have different ways of seeing things and different preferences. I find 2 or 3 spaces to be just about right. It looks good to me and I easily see where the blocks are. Many people like 5 or 6 spaces. To them that works well. To me it is painful, all that right-left movement my eyes have to do to read such code is distracting and makes it harder to follow.

        I’m not right. I’m not wrong. Neither are the others. Tabs combined with a good editor can accomodate us all. Spaces do nothing for this problem. Using spaces, if a wide-indent loving person is in charge.. well then F me. On the other hand if I am in charge (yet we are for some reason using spaces) well F the wide-indent lovers.

        How backwards is that!

        Just use spaces, configure your editors and everyone can be happy! Why would anyone be against that?!?!

        1. The flaw in your argument is that it makes it virtually impossible to include tables or columated text. Now IF the editor was smart enough to use tabs up to the first non-white space character, then use spaces for ALL following text on the line, it would work. I’ve not seen any editors configured out of the box that work that way.

          1. What?!?! I’m only talking about indentation. That IS exclusively the text to the left of the first non-white space character. Of course one should use spaces after that!

            And what do you mean about editor configuration? Good editors let you pick how wide the tab character is on the display. Usually this is in terms of number of times the width of a space character. I like 2 or 3 space indents so I set my preference to 3 and visually, when reading code that is 1-tab character per level of indent what I see is exactly identical to what I would see if the code was typed with 3-space character indents. The beauty is that my coworker who prefers 7 space indents sets his preferences and he sees something visually identical to code that is type using 7 space characters for each level of indent.

            It sounds like you are talking about the editor choosing for itself which whitespace character to use each time you press either the space bar or tab button. I’ve seen editors that do this to the indentation (the area before the first non-whitespace character). Mostly out of the box they ruin it by turning tabs into spaces. I’ve never seen one that takes your spaces that occur mid-text and turn them into tabs or vice versa!

            ^^ = 1 tab character for easy visualization because I don’t want to find out what wordpress does to actual escape codes in a non-editable comment, space characters are represented by actual spaces

            class foo(){
            ^^string bar(){
            ^^^^/* A table in some comments
            ^^^^ In |Out |Total
            ^^^^ 1| 2| 3
            ^^^^ 1| 1| 2
            ^^^^ 3| 1| 4
            ^^^^ 1| 2| 3
            ^^^^ See, it isn’t that hard!
            ^^^^*/
            ^^^^return “Goodbye Cruel World!”;
            ^^}
            }

            If you can’t type that on your editor you need a new editor!

            And if this looks messed up on your screen I blame the fact it is being viewed in html. It wouldn’t do that in a plain text editor.

          1. Not really. I recognize that my own preferences are not universal. I think they should recommend tabs so that we can each configure our text editors to give us our code our own way.

  5. Python 2 is still the default pre-install on, for example, OSX. If it came with v3 (or both) the switch would be a no-brainer. It is not nice if some simple script has to come with a list of “install this also” instructions.

    1. Well it’s either install a library or reinvent the wheel every time someone wants to write a script to read a url or manipulate a matrix. You can get a lot of commonly used libraries all packaged together (Conda, anaconda, &c) but is it really that hard to type ‘pip install SomePackage’ ? How often are you really installing libraries?

    2. Isn’t it fairly standard for the OSS bits and pieces OSX includes to be relatively ancient?

      My impression from when administering some was part of my job was that(unlike your average awful embedded widget) Apple at least considered what it did ship as something it had responsibility for; so you usually didn’t get ancient versions and ancient vulnerabilities; but that you could pretty much just use macports or fink if you wanted a vaguely recent OSS userland experience; while Apple only really cared about their languages in terms of enthusiastic updating(so Objective C and Swift; updates available except for older OSX versions that Apple would prefer you upgrade; but much more limited first-party support for python and others).

      1. Because the title “Stop writing classes” is the TL:DW and the video clarifies it better than I could. The first 10 minutes does a good job elaborating with the remaining 17 minutes offering better solutions to classes that don’t do anything and redundantly named exceptions.
        You may not have 27 minutes to watch the video but if you use python these 27 minutes might save you from falling into the anti-patterns it addresses, so you might want to find 27 minutes to watch it. If you don’t use python, then this comment isn’t aimed at you.

      1. Python works just fine using OOP.
        The talk doesn’t say you should never write a class, the speaker just implores you to “stop writing [bad] classes”. Writing Classes like the example code in the article is the type of class you shouldn’t write.

        Class InvalidSalaryError(Exception):
        pass
        or
        Class foo:
        def __init__:
        a = none
        def Do_Something():
        spam = a + 5

        These just clutter up your code base making management difficult for anyone else or yourself in 6 months when you forget what these lines do but you’ve imported them in the rest of your projects.
        These classes don’t do anything that couldn’t be written in 2-3 lines of stand alone code that’s more readable but rather than have me transcribe the talk, just listen to what the speaker has to say. You wouldn’t write C like you do perl so why do people keep trying to write python like C / java?

  6. I swear this is because they changed the print function. Every beginner saw that the most basic thing had changed to be longer and said “nope”, and the community is still feeling the effects today.

    1. Breaking compatibility when you need to is a good thing, if you don’t get rid of the trash it ends up being all you have. Having upgrade routes would be the best, but it’s very difficult, maybe something more like a staged deprecation like Java could have worked better, but that takes years and many versions.

  7. Most people failed to adopt Python 3 for a reason not addressed here. Some library they used (i.e. Numpy, Pandas, PIL, …) hadn’t yet been ported over from 2 to 3. I do believe most (if not all) of the examples I listed above have been ported over, but it took a long time for a lot of libraries to be ported over. That lag discouraged a lot of people (including myself) from switching over.

    Now my only excuse is that my fingers don’t want to transition to Print as a function instead of a keyword…

    1. THIS RIGHT HERE.

      I work in Python a lot, and the 2 to 3 transition– the items (nicely!) laid out in the article– is not a big deal at all.

      The holdup was all about package compatibility. Python3 is so, so much better.

      As for “I don’t like formatting with indentation,” um, well, I don’t like mustard, but I don’t generally like vinaigrette made without it either. We must not be dogmatic.

  8. My department in our company started with Python 3.3 (moving to 3.6 shortly) to solve a headache for us. Therefore we don’t have any conversion problems. That said I don’t really understand some of the comments above, because Python is very easy to read, write, and use and just about any add on module you could think of. But, as with any language, you can make it as obtuse or hard to use as you want. We have standardized on Python for our use ‘when applicable’ for its ‘power in simplicity’ syntax and ease of maintenance. As an example, in one of our systems, we had a bunch of batch files, excel/db VB apps (yikes!), Perl apps, C# apps that all had to work in concert being called from the Windows Scheduler. A nightmare to figure out the rabbit trail of who did what where when you had track down an issue. Over the past years, they have all been replaced with Python. Each program does everything it needs to do (not spread out over x number of apps). Working out very well for us. Easy to go check on a process. Call outs (called back to the office anytime of the day) are just about non-existent on that system now. And when there are our it usually turns out it wasn’t our problem (say someone else’s sftp site was down is why the data wasn’t flowing). And if a change needs to be made, it can be made on the fly, on the system that has the problem. No need to have a compiler handy or compile on development machine and then ‘re-deploy’ the app.

    I don’t allow tabs in our code base (C/C++, Python mostly) . All tabs do is mess up the code and cause headaches down the road for someone else. In C/C++ you ‘really’ see this very aggravating problem when trying to make sense of someone else’s code. Spaces work very well. Tabs should be ‘outlawed’ (or the editor set to tabs replaced with spaces).

    The ‘pip’ install process makes it easy to get most modules that we need taking care of dependencies for you.

    Since we use Windows… I can copy the ‘python folder’ (all set up with needed modules) to any machine, any drive, and Python is ready to be used there. Another plus for us.

    Code blocks should be lined up for readability, and Python forces this which is a good thing.

    As an object oriented language, I like it’s simplicity. We don’t go out of our way to use the most hard to understand constructs in Python. We keep it simple, even if it uses a bit more code or is a bit slower. In the back of our minds is someone else down the road will need to maintain this code and he/she may not be a trained programmer.

    I do wish it was strongly typed, but there is always some down-side to any language.

    These are just my thoughts though from years of coding!

    1. Python is strongly typed.
      Open up an interpreter and try 1+’1′. Java will coerce it behind the scenes to give you 2 but python will throw an exception.
      I believe you are talking about statically typed which no interpreted language does by definition. I don’t understand the fixation of static v dynamic typing, they do the same job you just get your errors at different times. Keeping track of what data types your using isn’t that difficult, is it?
      As for speed there are lots of things you can do to speed it up using pure python or you can use libraries written in fortran / C (NumPy). Most bottle necks are due to people trying to code python like they code C++ or java. There’s a pythonic way to do things and it’s generally faster than transcribing other language grammar into python words. When speed really matters you can write in cython, intersperse faster languages into your python code (fortran-magic one such library), write a C extension, or just switch to the faster language for that whole project.

    2. Yes. Adding in tabs when the rest of the code uses spaces does cause problems. That is why ALL of the indenting should be converted to tabs and every time someone is caught using a space they should have another finger broken!

      Tabs work great when you don’t try to mix them with spaces. Use 1 and exactly 1 tab per level of indent. You can configure your editor to display that tab as exactly the number of spaces that per/indentation looks good to you. And.. you coworkers can configure their editors as for what looks good to them.

      Spaces are for people who just want to force the indentation that looks good to their eyes on others. It makes the code harder to read for people who don’t see things the same as yourself.

      1. > Adding in tabs when the rest of the code uses spaces does cause problems.

        Only if you mess with the meaning of the natural constant “tab”. It’s the same as saying chillies in the hand cause troubles because you once handled them with an open cut…

        > Spaces are for people who just want to force the indentation that looks good to their eyes on others. It makes the code harder to read for people who don’t see things the same as yourself.

        Right, that’s a completely different topic from brace placement in other languages …

  9. I already have seen code which runs faster on Python 2 than on Python 3. But PyPy was even faster. ;-)

    But I agree that there is no need to write Python 2 only code, even if Python 3 features are not needed, especially that usually Python 3 code runs on Python 2 without any modifications.

  10. There is a small regression:

    def display_image(name, (x, y), mask):
    #…

    is valid Python 2 code whereas there is no automatic parameter unpacking in Python 3. (It’s called “automatic tuple unpacking” but I don’t know where else it is relevant—apart from assignment which -fortunately- did not break.)

  11. From time to time, programming language developers do this, and it is morally indefensible, but perfectly in their right.

    Inform, a language for interactive fiction, went from C-like to natural language. A more extreme change than Python, No inform 7 code will run in Inform 6, but Python 2 and 3 are still essentially different dialects, & maybe they’d be better off if the developers didn’t try to gloss it over, and embraced that they aren’t the same.

    Declare a new language instead of abusing version numbers to mooch off the glory of a language that will be simultaneously derided.

  12. For those really worried about Python2/3 and compatibility there is a library for that. Write once and run on both! http://python-future.org/index.html

    Also, if you’d like a little functional programming in your Python there is coconut which also happens to solve the 2 vs. 3 issue. http://coconut-lang.org/

    As for me, I’ve moved on to Perl6 and am having more fun solving programming than I’ve had in a long long time. Runs on Raspberry Pi, too!

  13. I think those print examples are not quite equivalent. They do print the same output but I think you should write the example of Python 2 syntax’s equivalent in Python 3 like this:
    print(“Fresh Hacks Every Day”)
    print(“Foo”, “some more text on the same line as foo”)

    or the example of Python 3 syntax’s equivalent in Python 2 like this:
    print “Fresh Hacks Every Day”
    print “Foo”,
    print “some more text on the same line as foo”

    So maybe just one a bit longer example:
    Python 2’s syntax:
    print “Fresh Hacks Every Day”
    print “Foo”, “some more”,
    print ” text on the same line as foo”
    Python 3’s syntax:
    print(“Fresh Hacks Every Day”)
    print(“Foo”, “some more”, end=”)
    print(” text on the same line as foo”)

    Also sep and file arguments are useful sometimes. I learned the Python 3 way and always import print_function from __future__ if I need Python 2 compatibility. That solves the most of the compatibility issues with print. Six solves the rest. Well, writing Python 2 and Python 3 compatible code should be for another blog post. Also sometimes you don’t want to stay compatible with Python 2 just because it would make things harder (missing libraries for example). I advise to write for Python 3, and if you need Python 2, then make it compatible, but remember to check first that your requirements work with both.

  14. A year ago I had a professor that insisted we use Python 2.7 because he was switching until more libraries were available in Python3. I TA’ed his class in the spring and he insisted folks use Python 3. Major switch for him just that fast. As a practical matter, there is not that much difference. Just be aware of the differences and code accordingly.

    For folks that avoided learning Python because of the switch, you are missing out. One of the speakers at PyCon 2018 (and I do not remember who at the moment) had a great description of the language: “Python is the second best language to do anything.”

    Really good summary, thank you for sharing.

  15. All the people in this thread complaining about Python breaking backwards compatibility, once in 25 years, are probably the same people who bitch endlessly about how C++ is saddled with warts because of backwards compatibility.

      1. You don’t redefine a language keyword.

        temp = sys.stdout
        sys.stdout =
        print “foo”
        print “bar”
        sys.stdout = temp

        That is a bit ugly, so you wrap it in a function or a context handler.

  16. I dread to think how many people have been lost from the language for good after being subjected to the weeping sore resulting from trying to run other peoples code on a system which requires both versions to be always present and lacks a well defined way of reliably keeping versions in their respective box. The transition hacks work if you have control over the situation but rapidly fail if the black box program is in control of the system. It would have been far better to give the language a new name so that there was no doubt about the required support libaries for the version in use at any time. We, like many others, invested a lot of time in what appeared to be an elegant language and it has driven us nuts.

    1. Maybe not “lost from the language”, but I can assure that my opinion of the language get much worse after just trying to run a simple piece of code and having to deal with that version problem. Just starting the .py file with a line specifying which version it uses, and then having the interpreter running the correct code branch would have kept it easier and compatible for much more people.

      And the developers could have focused on adding things, not just changing things.

  17. A big problem is that some newer Python releases, at least for Windows, rely on functions in the operating system that are not present in older O/S versions, like XP, Vista or even Windows 7.
    Also when compiled with newer Microsoft tools, some external libraries gets incompatible with older O/S versions (probably because Microsoft want it to be that way).
    To make a universal and useful programming platform you should ban all developing tools that sets limits to backward compatibility that have no technical reason (but mainly a strategic).
    It is very frustrating to get a message from Windows that the program is trying to call a function (often basic file-handling functions) in some DLL that isn’t present because some lazy programmer didn’t bother to even try to make it backward compatible.
    (And please don’t give me the usual you-should-have upgraded-a-long-time-ago rant. Those who still use older O/S versions, like XP, probably have good reasons why they do.)

  18. No one ever wants to pay for infrastructure. Rewriting what is currently working is an expense that businesses won’t incur. Its like asking someone to rewrap their one ply toilet paper to be two ply. Ain’t nobody got time for that.

    1. All you have to do is be lazy make a new program that determines which python version to use (I would add a header for the new version) and launches the appropriate one.
      Make a bracket that holds two rolls wrap the bottom one over and upper one under, pull from the center.

  19. Blegh, reading this article just makes me dislike python even more, i get how half the examples are from an old version the article specifically states to not use anymore, but by god thats some super terrible code, and the ‘new’ version really isnt that much better? i totally get how ‘some flavor of C’ can be daunting to new programmers, but by god, teaching them this tripe doesn’t help anyone, if anything it makes it harder for them to move on to a more powerful / less weird language? And yes, i totally get how good indentation is important, but making it a mandatory thing is just terrible design of the highest order.

    Im sorry at all python folks i just disrespected, but yea, wow this language is shit…..

    I wish the guy behind python stepped down years ago and the project already faded into obscurity, lets just hope that happens soon now that he did leave.

    1. I am sorry too. Sorry you feel that way. I found Python 3 to be a very easy and powerful language to come up to speed on. Not hard on the eyes (if written with that in mind of course). Forcing the indentation is a good thing. I mean, it is something we learned by experience years and years ago, but some seem to write code which is like looking at a spaghetti pile (tabs and all). At least Python forces some sane structure to the code. A block is lined up as it should be. Then there is objects. The object (classes) concept is way easier to use than in C++ in my experience. Try it, you may actually like it!

  20. As someone who does not use Python a lot. my biggest bugbear was how Python 3 broke compatibility with Python packages. I tried writing some Python 3 a few years ago, and I got in a hell of a mess with library incompatibilities.

    OK, things are probably better now, but there are still some packages out there which are not compatible.

    The other issue is most training is still in Python 2. So the question is, is there any feature in Python 3, which makes it a must upgrade? If not there are benefits in sticking with the largest common denominator of Python 2

  21. My favorite Python 3.6 feature is the ability to unpack a dict in declaring another dict. For example:

    new_dict = {**dict1, **dict2}

    I only started to learn Python 3 after 3.6 had come out and this was just such a natural way of doing things that I hadn’t even realized it was a brand-new feature!

Leave a Reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.