"We Fear Change." - Garth Algar
I can recall a time in the not-too-distant past when I called myself a die-hard 2.7 guy. You know the type - it's possible you are the type - slamming their mugs on the oaken table at the meeting of the high council, declaring loudly and dismissively that 2.7 was already good enough, hell, had always been good enough, for their father and their father's father before him. For generations 2.x had put food on our table, and here were these outlanders in their strange clothes with their stranger customs telling us we needed to put parentheses around our print statements.
I had read about 3.x, we all had. No one admitted it of course, but in secluded spots when nobody was looking we fumbled hurriedly through the pages in the forbidden chapters of our favorite Python books, drawn by the naughtiness of it all. I would never indulge this curiosity, of course. The coolest libraries, it was said, only supported 2.x. There was never a need. Then I took a new job.
I have been writing in 3.5 exclusively for a year now, and honestly the transition was trivial. I got hung up on Python 3's import changes (No more implicit relative imports) a few times, but it all feels quite natural these days. The sun still rises in the east as far as I can tell.
That said, there was nothing particularly compelling or sexy about Python 3. If anything, it required more troubleshooting (read: googling) to get things to work than 2.7, where things just worked. I figured it was building character or something and I kept at it. Then one fateful day I stumbled across the release notes for 3.6, and that old feeling of naughtiness stole over me - I peeked inside.
The Quest for Readability
If there is one tenet I hold above all others, it is that the code should work. Second and only slightly less important is Readability. I capitalize Readability because I consider it sacred, higher even than cleanliness in its proximity to godliness. I will write more about the blessed R elsewhere, but anyone who has opened a module written by their predecessor and felt the dream die will know what I am talking about.
Unfortunately, improving Readability often increases verbosity. Variables names get longer, lines get longer, and soon we're rubbing up against the 79th character so often the huge guy with the PEP8 nametag is doing practice swings in the corner with his blackjack.
I mentor several newer Python developers on my team, so I will regularly sacrifice finesse for Readability. Clever code tends to confuse, frustrate, and demotivate initiates. This is all well and good, but what about those situations when trying to make my code Readable actually makes my code unreadable? Allow me to explain.
In the above example, I am being very explicit about what my function expects, and how my URL is constructed. You could trip and fall into my module and probably manage to figure out what it's doing. Sadly, the string I've built is putting some serious strain on the buttons of its dress shirt. Cram a few more characters in there and we'd have a full-blown, hot-pocket-induced tragedy on our hands. Python 3.6 - like one of those svelte personal trainers with annoyingly specific certifications - is here to solve the problem!
"Get to the point." - Anonymous
Python 3.6 introduces interpolated strings. The f-string (short for formatted string) allows named placeholders in strings that are evaluated at runtime. Though literal string interpolation has been around for years in languages like Ruby, Scala, Groovy and BASH, Python 3.6 finally brings concise, Readable, formatted strings to Python users. Watch as my handy URL builder suddenly shrinks in size, and becomes more intuitive:
Because our codebase is glutted with verbose formatted strings, this addition represents a significant increase in the blessed R, which means an increase in Maintainability. I've been traipsing through our repository ever since, giddily shrinking strings.
I Can't Keep Track of All the Zeroes
Some people work with large numbers on a day-to-day basis. I am not one of those people, but if I were, Python 3.6's underscores in numeric literals would be a godsend. Now, instead of trying to count the zeroes and determine what the hell 10000000 even is (and probably miscounting and causing the world to explode at runtime), I can assign a value of 10_000_000 and Python reads it as 10000000. While nothing spectacular or dramatic, this was done in the service of the Blessed R, so Georg Brandl and Serhiy Storchaka are heroes in my book.
Async Therefore I/O
Traditionally, generators and comprehensions performed blocking actions. If I iterated through each item in my grocery list and placed it into my shopping cart, it was a one-at-a-time affair. No longer.
With asynchronous generators and asynchronous comprehensions, the actions taken on any given iteration do not block those for the subsequent iterations. This means I can grab that bag of Salsa Verde Doritos while the carton of grass-fed free-range eggs are still on their way to the basket. Asynchronous programming comes with its own set of pitfalls and perils, so make sure you do your homework before you decide to put these babies into production.
The Moment of Truth
I was worried when I spun up my new virtual environment with Python 3.6 on board and kicked off our automation suite, worried that something in the new version wouldn't play nice with our dependencies, but luck was with me today.
It just worked.
So to those of you slamming mugs and decrying Python 3 as heresy, I hope you'll forgive me my strange clothes and stranger customs. I've gone native.