The Contradictions in ‘The Zen of Python’
Software engineer Christopher Neugebauer got a surprise after delivering a talk at the PyCascades Python conference in Vancouver this March. A Python core developer thanked him for providing a framework for how to make choices about coding styles. That core developer said he’d once written a blog post — only to be told by his partner that he’d tried to justify two entirely different (and contradictory) style decisions.
But even more confounding: he’d found support for both styles in the canonical “Zen of Python” principles that for over two decades have been cherished by the wider Python community.
First released on a Python mailing list in 1999, the long-standing (but unofficial) “guiding principles” written by programmer Tim Peters seemed to capture the language’s philosophy in a series of 19 succinct aphorisms. Statements such as “If the implementation is hard to explain, it’s a bad idea.” Barry Warsaw, a software engineer on the Python Foundation team at LinkedIn, once even set them to music.
Yet Neugebauer warned his audience that while it’s easy to think there’s a “prescriptive” design philosophy that will always dictate the syntax for good Python code — there isn’t. At PyCascades he subjected those principles to some informed contrarian scrutiny, providing his audience with compelling examples of contradictions.
But along the way there were also larger lessons about practicality, communities, and how programming languages grow…
Contradictions and Subjectivity
“My biggest goal has been to get Python developers to start thinking critically about the qualities of their code,” Neugebauer said in an email interview last week, “rather than simply repeating lines from the Zen of Python to justify how they write their code.”
Neugebauer is also a former director and vice chair of the Python Software Foundation — and yet his talk’s description cautions that “even with prescriptive rules, writing ‘Good’ Python remains a subjective exercise.”
“When I first spent time thinking about the Zen of Python, I thought they were rules,” Neugebauer said in his email, “until I eventually found the contradictions staring me in the face.”
Here are a few examples he provided:
- Neugebauer’s talk explores a scenario where Python’s function-modifying decorators could make longer code samples unreadable. And this seems to violate Zen of Python principle #7: “Readability counts.”
- While some argue those decorators “modify” a function’s behavior, Neugebauer’s talk counters that they flat-out change functions, discarding the original entirely while replacing it with something completely different. (Neugebauer provides the example of a function which once returned a value, suddenly modified into returning a list!) Yet this isn’t obvious from Python’s decorator syntax, which Neugebauer says clearly violates principle #2: “Explicit is better than implicit.”Instead, he sees Python’s decorators bringing dreaded “implicit behavior” to the very act of defining a function — since what the function does ultimately depends, entirely, on what its decorator does.
- What about Zen principle #3: that simple is better than complex? In 2015 Python added an optional code syntax for providing “hint” about the type of a variable. Neugebauer agrees it “resolves ambiguity that was previously difficult to resolve.” But he also puts up a slide arguing “That’s not simple any more.” Providing type hints, as Neugebauer sees it, is “an extra piece of complexity that just wasn’t there previously.”Neugebauer warns that this could create code that’s crowded, verbose, “or other things that remind people of Java.” And this is despite Zen of Python principle #1: that “Beautiful is better than ugly.”
Later Neugebauer concedes that type hints “solved a real use case that real Python developers could not solve with Python.” Indeed, before type hints there was a clear lack of explicitness about types in Python code bases. “The solution isn’t simple. Good type systems are inherently complex.” But without it, the complexity moves into an even more crucial area. “Bad type systems make it complex to write code.”
As Neugebauer says later, accepting any one of the Zen of Python theses as your favorite “often means giving no weight at all to another of them.”
Neugebauer then adds the perfect quote from Tim Peters’ original email announcing of the Zen of Python principles. “If the answer to any Python design issue isn’t obvious after reading those — well, I guess just give up. <wink>.”
Wait, so where does that leave us? Neugebauer tells his audience that “If you take the Zen of Python at face value, you have to say that it has absolutely failed to guide Python in the right direction.”
And Neugebauer is prepared for the criticism that he’s only citing special cases. Principle #8 specifically states that “Special cases aren’t special enough to break the rules.”
“But almost as an afterthought, it also says that ‘Practicality beats purity,'” Neugebauer points out (in Principle #9), suggesting that the true message there is “not everything fits into those catchy little boxes that the Zen of Python has created for you.
“It means that sometimes, you need to make choices.”
This, Neugebauer concludes, is the “escape hatch” that’s built into the Zen of Python for us all. So while simplicity and explicitness are clearly part of the language’s design decisions, practicality is also a value. Neugebauer takes a stab at the hidden message: “Not every design problem fits into rules. Or, put another way, Python doesn’t tell you how to do things.”
Yes, the Zen of Python does specify that “There should be one — and preferably only one — obvious way to do it.” But even there, in what turns out to be lucky principle #13, there’s some clever wiggle room in the odd spacing around the dashes. Zen of Python author Tim Peters pointed out in 2015 that “most (but not all) American authorities say no spaces should be used. That’s the joke. In writing a line about ‘only one way to do it,” I used a device (em dash) for which at least two ways to do it (with spaces, without spaces) are commonly used, neither of which is obvious — and deliberately picked a third way just to rub it in.”
Experimentation and Obviousness
But there’s also an important lesson about communities here. Neugebauer argues that for there to be one obvious way, “you need to try out a bunch of different other ways first.” In fact, there’s an even grander message in the way that Python’s threading and multiprocessing evolved, now probably best handled by the asyncio library. It’s become the “one obvious way,” Neugebauer says, because “someone decided the old way wasn’t obvious enough.
“Finding the obvious way involves experimentation, and a certain acceptance that there needs to be a way to do something.” In the end, this is how any programming language evolves, he adds. “And generally speaking, Python is a language that gives you the tools that you need to bend things to your will.”
If you’re looking for a “prescriptive” set of Python rules to follow, Neugebauer recommends instead PEP 8 — the official style guide for Python code. “It tells you what to do,” Neugebauer reminds the audience. “It’s the very definition of a prescriptive PEP.” It even references the Zen of Python (by its official designation, PEP 20) — agreeing that “Readability counts.”
But Neugebauer has set up his audience for a contradiction yet again, since right there on the style guide’s first screen is a section warning you off using it as an ironclad set of rules. Its headline?
“A Foolish Consistency is the Hobgoblin of Little Minds.”
A style guide is about consistency. Consistency with this style guide is important. Consistency within a project is more important. Consistency within one module or function is the most important.
However, know when to be inconsistent – sometimes style guide recommendations just aren’t applicable. When in doubt, use your best judgment. Look at other examples and decide what looks best. And don’t hesitate to ask!
In particular: do not break backward compatibility just to comply with this PEP!
Neugebauer says he’s ultimately making his stand against “mindless” consistency. “It means that you need to think, and you need to consider whether it makes sense to be consistent.”
And Neugebauer leaves the audience with a warning. “Python is not prescriptive, but it’s easy to think that it is. It isn’t… Remember that different people have different use cases, and Python grows as a language as a community because we support those use cases.”
So what do you do when confronted with two conflicting opinions? “Value practicality over purity,” Neugebauer says at the end of his talk.
Which, again… is Zen of Python principle #9.
- Panel discusses “finding a job while caring about free software” at the FSF’s annual Libreplanet conference.
- 30-something programmer shares 10 lessons they learned from boomer developers.
- Free software advocate Lori Angela Nagel explores “how to have deeper conversations with anyone about free software philosophy.”