Grokking Pythonic
As long as it works, why does it matter what it looks like?
What does work mean?
Pythonic: a coding style that leverages Python’s features to make readable and beautiful software.
Pythonic code:
The Python programming language evolves through Python Enhancement Proposals, which provide a mechanism for public discussion and peer-review of language changes and new features.
Surprisingly, not everyone on the internet agrees, and sometimes the discussions can get a bit heated and deviate from civility. See the Walrus Operator.
Although beauty is subjective, this so post does a good job to explain the main attributed of beautiful code, which includes:
Clarity and Transparency
Elegance
Efficiency
Aesthetics
It can take some time to develop the beautiful python taste. Study professional codes and keep programming.
Explicit code means the abstractions are conistent and clear. It doesn’t mean everything is spelled out (you don’t have to understand bytecode, assembly, or semiconductor physics for your code to be explicit).
Simple means the most direct (shortest lines of code, most readable) is preferred. This means avoiding complex features when possible (i.e., dictionaries and numpy arrays are better than custom classes for simple cases.) However, sometime the correct behavior is complex, so complex code is unavoidable. In this case, it can just be keept as simple as possible (for the user).
Highly nested structures are complex, and they often require recursion to effectively navigate. This should be avoided when possible. In scientific computing, numpy operations are strongly preferred over nested for loops, both for efficiency and readability.
Sparsity means fewer things. Fewer classes, fewer functions, fewer parameters.
Readability means code is optimized not just for a computer to understand, but for a developer to understand. Documentation is a first class concern.
Any fool can write code that a computer can understand. Good programmers write code that humans can understand.
– Martin Fowler”
Consistent means abstractions are clear and not full of special exceptions. Software shouldn’t be like the English language.
Pragmatic means prioritizing functionality and usability over more esoteric concerns and unlikely edge cases.
Errors should be raised unless the way to deal with them is obvious.
Guessing what a user may want when there are several viable choices is rarely a good idea.
Intuitive software is best illustrated when doing new things seldom requires referencing the documentation; the abstractions, object names, and APIs are such it is obvious how certain things should be done.
Python’s creator, Guido Van Rossum, is Dutch, hence the inside joke in the PEP.
As the old adage goes:
Python’s creator, Guido Van Rossum, is Dutch, hence the inside joke in the PEP.
As the old adage goes:
Usually it is better to have something that works for 90% of the cases now than a perfect program sometime in the distant future (which often means never).
On the flip side, implementations that are too hastily done can be hard to change. Especially when they must be maintained forever. Like an angry email to your boss, some code is better left unwritten.
Working through a hard programming problem? Grab a friend, or a rubber duck and explain your implementation. Often simpler approaches will surface.
Coming from MATLab, I found having to import python modules annoying. Why is it np.pi
rather than just pi
? Then, I created a variable named pi
in a matlab finite element code which, of course, overwrote the expected value. It took me several hours to debug, but afterward I understood why namespaces are a “honking” good idea.
PEP8 is python’s style guide. While originally only applicable to the standard library, it is now widely considered good practice for (nearly) all python projects.
Here we explore a few of the more important aspects of PEP8, but you can find the whole thing here.
__future__
)All of this isn’t actually in PEP8, but generally accepted by many (e.g., the CIA)
_
indicates non-public object__mangled
- leading __
performs name mangling in classes__variable__
) should be avoidedtry/except clauses should:
wrong
Negative membership checks
Bool checks
A style guide is about consistency.
When to ignore guidelines:
Shed can be installed with pip:
Then run it while in your repo with
Style/design issues in code that “works”, but isn’t pythonic (elegant, efficient, readable, …)
How to measure?
Complexity deodorant
What does this do?
Add meaningful names and docstring
Even Better, use scipy!
Incomprehensible Comprehensions
Break into different lines
Wrap in a function
If still unreadable, remove comprehension.
Signature: inputs and outputs of a callable (can include names and types)
Common problems:
Too many input parameters (probably need different functions)
Output changes based on parameters
None returned rather than raising an Error
Not using language/std lib features:
Nested Dict: Bad
The collections module of the standard library is full of goodies.
Filtering: Bad