Python, as Reviewed by a C++ Programmer

  • Posted on: 21 April 2017

I haven't written here in 8 months. Part of the reason for that is that I've swapped jobs. At the old place, I spent about 90% of my time writing C++, and about 10% of the time using other languages, including Python, Perl, web languages, and last, and oldest, Fortran.

Aside: Did anyone see that movie, Hidden Figures? I loved it. But it made me a little uneasy when I realized people have been writing Fortran for about 60 years. I felt like a part of history? Not sure if good or bad.

Anyway, at the new place, I'm about 50/50 between Python and C++. It was trial by fire for me learning Python. While I don't think anyone can faithfully 'review' a programming language, I wanted to write down some of my first impressions of Python (as more than a dabbler), as a guy coming from C++.

Okay, here goes:

A Bit About Language Features.

I don't want to spend a lot of time on language features. This isn't exactly meant to be a Python tutorial. I want to take a little bit of a higher level view in this article, and add some reflection about the two languages that maybe couldn't be found in a book or tutorial.

But there are some things I was caught out by as rookie Python developer, and I'd be remiss if I didn't at least mention them. Of course, details about them are widely available. I may add to this list!

I Didn't Realize it, But I Used the Compiler as a Refactoring Tool.

Over the years of writing C++, It seems one of my main refactoring workflows had evolved into:

  • e.g., Rip out some members or methods in a C++ class.
  • Try to update or refactor some of the now-old use cases I know about.
  • Compile, in order to see where else the old member or method was used, and then fix up those instances.

I suppose this is not how everyone operates, but when I found something in the interface of a class that was flawed or redundant, I would often start by just ripping out the offending part of the interface, and going from there. Obviously, that's not part of the deal in Python.

In C++, there is some ideal that if a program compiles, it should do what you think it will do. In reality that's of course not always true, but it's something to strive for.

In Python, if you delete a variable or function, you have to search around for all the instances (often tricky, at least for a newbie like myself), and do the refactoring. Even then, there's a chance you didn't do the whole job, due to the duck-typing nature of Python. You will only notice you've forgotten to update a part of the code when you run that piece of code, and it bails out.

On the Flipside, No Compiler!

Despite our best efforts, us C++ folk are still often plagued by slow compile times. Maybe this gives us a chance to chat with coworkers, or maybe you're a top-level multi-tasker, and you can focus on getting stuff done while another compile completes, but I think most would agree losing a good chunk of the workday to compilation is a Bad Thing.

With Python, at least in theory, it's write and run. In this respect, I have to say I have really enjoyed the fast turn around time of Python.

For Me, the Ease of Installation and Use of Modules is the Best Thing About Python.

For me, this is the most crucial difference between C++ and Python. Just about anything you can dream up is pip'able, and import'able. And then you're back to thinking about the problem at hand again, which is great.

Say for example, I want to compute a FFT. In Python, this looks something like (at the command line):

pip install wheel   #Only needed if caching new packages
pip wheel numpy     #First time only, to seed the cache.
pip install numpy   #Takes less than 2 seconds. Amaze.

(Stolen from How to pip install NumPy in two seconds flat)

Then, in the code:

import numpy as np

# ... later...

np.fft.fft()

In C++, the answer is something like:

  • Shop around for an FFT library.
  • Settle on FFTW.
  • Do the configure, make, make install dance. That's easy enough, but FFTW will take some time install.
  • Update your build tooling to use FFTW.
  • Learn to use FFTW.
  • Compile and walk around your business park!

The C++ process here is about an hour-long process, and the Python one is about 5 minutes. I have even spotted C++ here to some extent, as FFTW is the defacto library for computing FFTs in C++. In other cases, searching around for a library, installing, and figuring how to use it is quite an arduous task.

C++17 won't feature modules, which are supposed to be, in some respects, C++'s answer to this problem. Although I have heard good things about conan.io, I must admit, I haven't gotten around to using it myself.

Python is Sometimes a Bit Too Flexible.

Sometimes, the Python philosophy seems to be "do anything!". Obviously, that can be good. But sometimes I have been bitten by the flexibility (and to some extent, the lack of verbose syntax) of Python.

As an example, consider the member declaration in C++:

class Klass
{
    public:
    std::vector<Foo> m_my_foos;
};

If I want to know what I can do with my foos, I just have to look at the declaration of Foo. I also, obviously, know I can only have Foos in that vector.

In Python:

class Klass(object):
    """
    Documentation, which feels more required in Python.
    """

    def __init__(self, name, balance=0.0):
        """Construct a Klass."""
        self.myfoos = {} 

I've initialized self.myfoos to be a dictionary. I can put just about anything in that dictionary. When I see myfoos later, perhaps in a class method, and I want to iterate over it, I'm not really sure what I can do with it.

That's a classic double-edged sword, and there are many, many more examples in Python.

I will say, Python seems to lean more on documentation and its required whitespace rules to provide some form to what could otherwise be a sea of madness. I sometimes see C++'s required verbosity as beneficial in this way, but I know there are just as many on the other side.

Python is A Good Choice For Many Project Sizes.

At least by title, I belong to the math/science/engineering sect. In reality, I'm in the fake-it-till-you-make-it business. In any case, we deal in graphs and plots a lot.

If I have an idea, and I want to go from 0 to 2D graphing something in C++, I don't know that I have a best course of action for that case. I have to shop for, choose, and learn to use something 3rd party-ish in order to proceed (see above). When you're trying to demo an idea in C++, it can be frustrating. You feel like you're spending a lot of time rooting around in the minutia.

I should say that within the tooling setup any life-sized C++ codebase has, you are never truly starting from zero, but I think the point still stands. Turn-around on new ideas can be hit or miss or painful.

On the other hand, I feel I can accomplish virtually any idea-scale task in Python quite easily. For my purposes, it is like Matlab and production level systems language rolled into one.

I also feel comfortable scaling Python to medium sized applications, but I'm a little uneasy about using it for very large applications. It could just be me. See below.

A Few Other Semi-Obvious Items.

  • Yes, you by and large have to distribute the source code for a Python application.
  • Coming from C++, the whitespace enforcement seems annoying at first. But you get used to it, and the code does usually come out nicer-looking. And, as I mentioned above, I think the emphasis on clean code and documentation in Python is meant to counter its extreme flexibility in some cases. I'm just speculating.
  • Yes, you often have the opportunity for better performance in C++. Whether or not its worth it for your particular application is the key question.
  • Anything else? :)

And Finally, an Outstanding Question:

At my current place, we have a big HPC-type application which features a host of custom algorithms, and performance is of utmost importance. C++ is the obvious choice there. We also have a few large GUI-based codebases that are in Python, which are less performance-critical.

I can never quite decide if Python is the right option here. As I've said earlier, the ease-of-use of modules is really indispensable, as is the quick turn-around and deployment time.

But on the other hand, the codebase occasionally feels like a million-line template library (speaking in C++ terms). The flexibility becomes somewhat confusing at this scale, IMO, and it sometimes feels like Python isn't quite cut-out for large-scale applications. I suppose, in C++, I used types as my guide, and often inferred from there. At large scale, the verbosity of C++ can be beneficial.

In Python, it's harder to reason about what, e.g., a member variable is supposed to do just based on its declaration.

There's a good chance this feeling just represents my relative inexperience with the language. Or maybe I'm getting old. In any case, I'm learning to love Python. It wasn't a hard sell, for the most part. And in my opinion, being a true polyglot is a must for any developer.