Skip to main content

Python 2 vs 3: the print war

Disclaimer

I will be upfront and say I think Python 3 made a terrible mistake by making is so that Python 2 scripts would no longer work. Most of my experience has been with Python 2 and I'm used to using the print statement to print a line to stdout. I hate that Python 3 makes you call print as a function. Yes, I know there are other differences and they're threatening to stop supporting Python 2 starting 2020/01/01. I don't know what I'll do after that point. I suppose Python 2 will still be around but it won't be improved and bugs will not be fixed and I may be ok with that.

What can you deduce from print(expression)?

I've started a new job, looking at new code, and was without knowing much about the execution environment, I was beginning to believe that they must be using Python 3 because I saw something like the following in a Python script:

  print(expression)
This seemed to be a reasonable conclusion. However, I also found code in the Python 2 style that did not use parentheses!!

How can this be? Do they have two different execution environments? I took it upon myself to try a little test to see definitively what each version supported and here's what I came up with:

# There is no pound-hash operator because I intend this script to be called using `py -2 print-test` or `py -3 print-test`
import sys
def test(stmt):
sys.stdout.write('\nTesting {stmt!r}\n'.format(**locals()))
try:
exec(stmt)
except Exception as e:
sys.stderr.write('Caught: {e}\n'.format(**locals()))
sys.stdout.write('Welcome to Python {version}\n'.format(version=sys.version))
# this will fail from Python 3 because `print` is a function call and there are no parentheses
test('print sys.argv')
# this will work from Python 2 because the parentheses are essentially ignored. `print` is still executed as a statement (not a function)
# and the parentheses just wrap a string. They're not neceessary in this example but they don't hurt but they don't help either. They could
# be used to express a tuple but an embedded comma such as `(expr , )` would necessary to accomplish that.
test('print(sys.argv)')
view raw print-test hosted with ❤ by GitHub

Python 2 behavior

$ py -2 print-test
Welcome to Python 2.7.15 (v2.7.15:ca079a3ea3, Apr 30 2018, 16:30:26) [MSC v.1500 64 bit (AMD64)]

Testing 'print sys.argv'
['print-test']

Testing 'print(sys.argv)'
['print-test']

Python 3 behavior

$ py -3 print-test
Welcome to Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 27 2018, 04:59:51) [MSC v.1914 64 bit (AMD64)]

Testing 'print sys.argv'
Caught: Missing parentheses in call to 'print'. Did you mean print(sys.argv)? (, line 1)

Testing 'print(sys.argv)'
['print-test']

Conclusions

As you can see, using parentheses doesn't hurt in Python 2. However they are required with Python 3, of course.

I guess the moral this excursion is: You can determine Python 2 is used if parentheses are not present in a call to print but you can make no determination if parentheses are used since it that will work with either version.

Comments

Popular posts from this blog

Dynamic Python script loading

I have a bunch of toys and tools in a Git repository - I affectionately call this my toys repo . Most are just scripts that I use from a Unix (Cygwin or Git bash on Windoze) command line but there are some Python classes that I sometimes use in another script. Today at work, I was coming up with a new Python script that made use of a couple of my public classes. The script is good enough to share with my colleagues but I'm faced with the problem of my public classes: I imagine that most of my colleagues haven't even heard of my public classes and I don't expect them to download the entire repo just to get a couple of classes If I'm going to distribute the classes as separate files I introduce new problems: It could be confusing to have the files tag along. What is the user supposed to do with them? The answer is nothing - they should leave them alone and make sure they are in the same directory as the main script in case they decide to move th...

Git-based version information from Python script

I had this idea of generating version information for a Python script that uses ArgParse . The code is a little more than I was expecting but I think it works well. Here is the code: Usage Here is an example of its usage if the script is part of a git repository: $ ./version-example --version b92798, master, 2019-01-18 10:35:02, ['origin:https://github.com/pfuntner/gists.git'] $ It contains: The SHA1 of last git commit that changed the script The current branch of the repository The date of the commit - I think the timezone element is present in this but I didn't want to deal with timezones so I'm ignoring it A list of the remote repositories This is printed on two lines but that's something that ArgParse is doing, not me. Here is an example of its usage if the script is not part of a git repository - we don't have much information to work from but we can at least get the timestamp of the script: $ ~/tmp/version-example --version 2019-01-...