Python Experience from a Julia Perspective

I wrote python scripts long time ago. The last year I’ve mainly used Julia for my scripting needs. I have a bunch of scripts I need to share with other people and I don’t want to require people to have Julia installed, so I am trying to rewrite my scripts to Python.

Here are some of my observations about using Python again after primarily using Julia for my scripting needs.

First Impressions

The built in python REPL is very bare bones compared to what you get out of the box after installing Julia. Julia gives you nice syntax coloring and completion. You can easily lookup docs for a function by hitting ? and it completes. Using Python’s help() feels rather clunky in comparison. Also I find reading Julia docs far easier and straight forward.

I get this from writing help(“print”) in Python:

While for Julia when I write ?print I get:

Since the built in REPL is terrible, I installed bpython. Nice syntax coloring and completion. It doesn’t feel completely stable though. I’ve had instances were everything seems to stop working properly and many times in the middle of executing commands, bypthon abruptly quits. Ironically I never experience this with the Julia REPL which isn’t even in version 1.0 yet.

Right arrow completion based on something written in your REPL history works, just like in fish shell and Julia. That is nice, I love that functionality.

I don’t like the way bpython defaults to showing results of operations and functions. Here is a REPL session to illustrate the problem:

See how when you assign to a variable you don’t get to see the result. Compare this with Julia:

I think that is the behavior you want. bython behavior strikes me as an odd default. Why would you not want to see results in a REPL? Isn’t that why you are using it?

Interacting with arrays

I need to check if an array is empty. In Julia that would be isempty. No such thing in Python. I try empty and isempty which would work in many other languages. Instead I have to know empty collections in python are false:

That fits poorly Zen of Python:

Explicit is better then implicit

Everything useful has to be imported

In Julia a lot more is imported out of the box when you start running it. That makes discovery of common functions easy. You just start writing what you think the function is called and get a completion. E.g. I want to see what sort of string splitting functions exist, but starting to write “spli…” gives no results.

One has to assume this splitting function is hidden in some library you need to import.

And when you do import something you have to be careful about how you name things. E.g. I got the error message:

I look at my code which said:

Looked okay, except it turns out that since I had written

rather than:

Python had no idea what os was. I had to write

This runs counter to what I would assume. Most languages I am used to, you can always qualify a name fully, whether it is Java, C++, Swift or Julia.

String literals complicated

In Julia you can write something like this “Hello how are you today $name”, while python requires using special formatting functions:

Poorly designed standard functions

In both Julia and Python you got functions to join strings as paths. E.g. in Julia you can write joinpath(“foo”, “bar”) to produce “foo/bar”, likewise you can use path.join(“foo”, “bar”) in Python.

One should think there was a natural extension of this for creating a path with the home directory. In Julia you can write e.g.:

But there is no such natural extension in python. Instead you have to write:

Another common thing to do for a script would be to e.g. remove a directory.

This is how you remove directory foo in Julia and all its contents. Pretty straightforward and matches how the rm shell command works.

With Python there is no rm to find and rmdir found in os package/module has no options to delete anything but empty directories. Instead we have to look at an entirely different module named shutil with the function rmtree. This strikes me as inconsistent. You have to look for a closely related function in an entirely different library with a non-intuitive name.

Integration with the shell

When programming Python using bpython I come to appreciate just how well integrated Julia is with the shell.

E.g. I was testing out code that made a temporary file.

And when working with it I wanted to quickly check the contents of the file. Shell commands like cat are in my fingertips for such things. In Julia I can write:

To look at the contents since the variables in Julia are exported to the shell as environment variable. I often use that as a way of editing files:

Opens the file for editing in TextMate. Or checking git status:

I could not find any similar way of interacting with the shell.

Python whitespace sensitivity isn’t worth it

I analyze my code by copy pasting it into the bpython. Since the copied code is frequently indented inside a function, it frequently wont run, due to Python’s whitespace rules.

I honestly don’t see the point. Making whitespace significant means the programmers is left to indent the space properly rather than letting the editor figure it out based on the syntax.

The classic C mistake of not enclosing a block of code with braces is trivially avoided in most modern languages today by requiring blocks to always be defined. E.g. Go and Swift both require curly braces after while, for, if and else. With Julia you always need to terminate with a end keyword.

Running shell programs is awkward

In my Julia script I need to perform code signing on an executable. On OS X that is done with a shell tool called codesign. I run it like this:

That will execute the command as if I ran it from the shell, dumping STDOUT and STDERR to the console in normal fashion. But there are many ways of executing a command depending on your needs:

Python on the other hand does not have the convenient back-ticks for working with shell commands. I can run call command but that will not print result to console.

Having to separate out each argument explicitly is also somewhat cumbersome. I ended up writing a simple utility to convert my Julia back-ticks command invocations to the comma separated Python argument list. To get more control over outputs you can use Popen in Python but it is far away from the elegant solution found in Julia. I mimicked the Julia run function with:

It was not obvious how this worked and I had to spend some time investigating it. In contrasts I already knew readlines and readall worked for reading files in Julia, so I simply tried to see if I could use them for reading the output of processes. This shows the power of a well defined orthogonal features. Julia functionality combined very well in predictable ways so that you don’t need lots of edge cases to handle different variants and data types.

That was one of the original beauties of Unix, that you could treat reading and writing to a network interface, sound input/output, files etc just as reading and writing to a file. In similar fashion Julia allows you do treat file descriptors and processes in a similar fashion. Even including variables is similar. Whether dealing with string literals or back-ticks for expressing a command line, you can refer to a variable foo as $foo and it will be escaped properly.

I can write `codesign — verify $app` to get a runnable shell expression or “codesign — verify $app” to show that expression to the user. In contrast Python distinguishes the two with:

Which has no shared consistency.

Conclusion

Python has a large user community and large set of libraries for all sorts of imaginable things. That makes it a practical tool for more things than say Julia.

However it does not feel like the fresh, easy to use programming language it once did when I started using it years ago. Measures up against expectations today, I found it wanting in many ways. For such a widely used programming language with a lot of resources behind it, it seems inexcusable to have such a poor out of the box experience compared to a relatively unknown programming language like Julia, with access to a fraction of the development resources.

Julia does not come preinstalled and is relatively unknown, which makes it a poor choice for scripts you want to distribute to other users.

But as a replacement for many of the things you might do in a bash script I think Julia offers a superior experience. Here are some examples of what I do in Julia which does not require extensive libraries:

  • Converting SVG images to PNG tools of different resolutions, utilizing a command line tool.
  • Modifying and resigning iOS apps.
  • Changing source code in various ways. E.g. obfuscating method names or string literals.
  • Modifying a git repository. E.g. as part of constructing a git repo from a subversion repository.

These are the sort of things you might have used bash for but which gets clunky because Bash control structures, string and array manipulations are awkward to use.

Written by

Geek dad, living in Oslo, Norway with passion for UX, Julia programming, science, teaching, reading and writing.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store