 There we go. Now we can see my talk. That is awesome. Okay. So the question is, what does it mean to write Pythonic code? If you've been using the Python language for any length of time, you have probably noticed that there is a canyon of difference between functioning code and Pythonic code. Tim Peters, now famous, or should I say infamous poem, Zen of Python is considered one of the pivious summaries of what constitutes Pythonic code, but I think sometimes it seems a bit open to interpretation. So in this talk, I'm going to be breaking down how I believe the 20 aphorisms, yes, 20, I swear that's not enough by one error, of the Zen of Python can be applied to your code right now. And even though this says on the guide, this is geared towards beginners, I think this is something that anyone can benefit from. So a little bit about me, like it was mentioned previously, I am the CEO lead developer of mouse palm media, which is my own open source startup. We make educational software and we are working on building an open source game engine as well. The author of Dead Simple Python, which is soon to be a book from No Start Press early in 2021, hopefully. And I can be found all over the internet as Cognouse92. I'm especially visible on dev and Python's chat room on free note IRC. You can find me all over the net, Stack Overflow GitHub, Twitter, very active there. You can find the complete list of all the things I do in all the places I am at my website. So in case you've never read the Zen of Python or if it's been a while, I'll just go ahead and read it to you now. Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules, although practicality beats purity. Errors should never pass silently unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one and preferably only one obvious way to do it, although that way may not be obvious at first unless you're Dutch. Now is better than never, although never is often better than right now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespace is a one honking, great idea. Let's do more of those. So to understand the Zen of Python, we have to start with understanding where this thing even came from to begin with. And this showed up in a June 1999 comp Lang Python thread, which was rehashing an eight-year-old debate about garbage collection. And developer Patrick Phalen suggested that Wido Van Rossum and Tim Peters should work out some sort of elements of style for Python, to which Tim responded with a notably snarky humorous attempt at it. It wasn't until 2004 though that it actually gained the name the Zen of Python and was codified as the PEP 20 we know and love. And fun fact, although you may already know this, if you run import this in any Python shell that will display the Zen. Funny thing though is if you read the source code for that Easter egg, it doesn't even follow any of its own advice intentionally, because we all love irony. But before I can really break down the Zen of Python, we have to answer this question about whether or not Tim Peters was actually being serious when he wrote it. Well, as Tim later described very Warsaw, it was a throwaway Python list post. But like all great triumphs of literature, it was written during commercial breaks while watching professional wrestling on TV and munching on a ham sandwich. All true. That's Tim Peters for you. Ironically, his post initially fell on deaf ears as that garbage collection wrestling match raged on. Like I said, it wasn't until 2004 that it was really codified. But whether this was intended as a joke or not, as Barry Warsaw pointed out, the Zen of Python captured the language as aesthetic through the eyes of one of the earliest Pythonistas. By the way, I should mention that Barry Warsaw also set this thing to music this past May. So you can find that on his website. He recorded a song, the Zen of Python, which I think we should all learn how to sing. Maybe we should sing that at our next conference. So I'm going to break down this statement by statement. But instead of going in order, I'm going to address several related concepts together. And I want to start right in the middle, as I believe the entire Zen of Python hangs on this one statement. Readability counts. Python code is readable above all else. Because the computer is not picky. It's happiest with binary any day. Programming is really the art of converting human thought to computer logic. Code exists for people. Whether they be co-workers, future maintainers, people learning from our code, or just ourselves two years from now. And it's really tempting when we're coding to try and write clever code. It feels cool to write clever code. It makes us feel smart. But as you're going to see throughout this talk, clever is kind of the very antithesis to Python. It doesn't mean that we shouldn't be writing brilliant code, but clever code can be a bit of a problem. Especially because it often sacrifices readability. Brian Carringen, the co-author of the C programming language, he said everyone knows that debugging is twice as hard as writing a program in the first place. So if you're as clever as you can be when you write it, how will you ever debug it? I would like to add, if you can't debug it, how is your successor supposed to? So later on in the Zen, Tim says there should be one and preferably only one obvious way to do it. This is one of the most quoted parts of the Zen, but what is obvious? Well, obvious is, I believe, all about finding the optimal solution to a specific problem. What is the best possible way to solve this specific thing I'm trying to solve on my code? The trouble with obvious is that it's not always obvious looking into it. It's only obvious looking back retrospectively, reading the code, you go, oh, yeah, that makes sense. Why did I think of doing it any other way? That's what obvious means in Python, as you'll see in a moment. Now, our mantra in Python is there's only one way to do it. This contrasts with some languages like Perl, where they say there's more than one way to do it. Do we save this in C++ too, which is my other language? But the trouble is, both Python and Perl ships working code, maintainable code. So who's right? I would actually argue that Python and Perl are not really on opposing sides at all, and understanding this will help make this concept of obviousness more easier to understand. Apparently, Wido van Rossem doesn't think that these two languages are on opposing sides either, because he said when asked about it, the funny thing is that while there's a lot of animosity in the lower ranks, I've actually been very friendly with Larry Wall and Tom Christensen ever since we met five years ago at the VHLL symposium that Tom organized. So if Wido's getting along with the creator of Perl, we probably should too. So like I said, these are not on opposing sides. And I'll show that by breaking down kind of these two mantras. So in Python, like I said, we say there's only one way to do it. We are looking for the one obvious way for each unique situation. The trouble is we don't necessarily know this right off, because remember, obvious is retrospective. So we have to try things out. We have to experiment to find the best way to do it. Over in Perl, they say there's more than one way to do it. And this is a way of keeping people from shutting down experimentation, because, well, we've always done it this way. But the goal is still looking for the best solution to that particular problem. It's not that we're just looking for any solution. It's that we're still looking for the best one. So you see that this is the same strategy. It's just the opposite focus. Python says find the optimal solution and experiment if necessary. Perl says experiment to find the optimal solution. So really, it's the same thing in many ways. And on this whole note of obvious, like I said, that one obvious way is retrospective, which is why Tim says, although that way may not be obvious at first unless you're Dutch. Once we know the solution, once we see the working code, then it all becomes clear. Now, Python's implementation details do advise this when you understand how Python does things behind the scenes. It helps you pick better approaches for your particular situation. But we're not all core developers. We don't all know the inner workings of Python, necessarily. We don't understand it better than most of us air go unless you're Dutch. So he'll get to the obvious way probably before most of us will. But then there's also this factor that the one obvious way is continually evolving. I have a copy of the Python cookbook from Python 2.4, ancient history, and all the solutions in there, of course, were the obvious way at the time. Python cookbook was very popular at that time. But it's no longer, none of these are any longer the one obvious way. The one obvious way has changed as the language has changed. So it's important to remember, too, that what's obvious yesterday may not continue to be the obvious solution tomorrow. So Python code is readable and it's obvious. It's also beautiful. Beautiful is better than ugly. Trouble is what is beautiful code? View these in the eye, beholder. It's a little hard to nail down. But however you define it, beautiful code is always a pleasure to read because we don't want to sacrifice readability. It can be really hard to nail down what beautiful code is sometimes, but you know beautiful code when you see it and you know ugly code when you see it. Most people would agree given one piece of code that yeah, yeah, that's pretty bad or yeah, that's pretty good. And in terms of writing this, this is why we have the pet eight style guide to begin with to help advise writing beautiful code. Although that's not the whole picture of beautiful code, but it's a good chunk. So this makes the most sense with some actual code. So here's some ugly code I've said. I take full responsibility for writing this monstrosity. What are the problems? Poor spacing for one thing. The lines are crammed together. We have multiple statements on one line and defining N and R on the same line. The conditionals, the header and the sweet are on the same line. This is just nasty. It's over complicated besides that. It's hard to read. I'm not using any of Python's particular abilities. I'm just writing C code in Python basically. This is not nice. Here's the same goal, but done beautifully. So this employs better spacing, especially around those operators. As you see later, white space is important. There's one thought per line. So it's much easier to break down what is going on. It's idiomatic. I'm using Python as Python. I'm using this list comprehension. I'm using the star operator. If you don't know what these are, you may have to read the documentation, but once you know the syntax, then it's very obvious what I'm doing. And this is easy to read besides that. Now, that is not to say that all list comprehensions are automatically beautiful, but I think this is one case where they are. So Python code is readable. It's obvious. It's beautiful. What else? Python code is explicit. A surprising behavior is always a problem. By the way, a little bit of a relevant rabbit trail. This is why I'm well-known around the Internet for my dislike of JavaScript. It's not because I'm picking on JavaScript or because, you know, some random personal dislikes of syntax. My issue with JavaScript is simply that it has too much surprising behavior. And even its experts will admit this. The surprises are bad in any language. Most of our issues with Python even come from surprising behavior. If you've ever tried to pass a list as a default argument to a function, you know what I'm talking about. Mutability can be surprising. So we want to avoid surprising behavior. And besides that, if you have to comment what the code is doing in order for it to be clear, then you really need to be rewriting the code. The code is not Python at that point. Now, that's not to say I don't believe in comments. But comments have a specific purpose. They should be describing the intention. Comment why not what. I like to use the rule that the comment, excuse me, should describe the behavior without having any reference to the actual implementation. And in effect, you're a good comment as part of a living specification. And if the comment doesn't match the code, then you assume both are wrong and you check and rewrite both to make them match. And then, of course, there's this issue of naming. Because usually when you're reading code, you're looking for a given behavior and the name is one of the first places you're going to look. Never send a comment to do a name's job. And yes, naming is hard. Fully acknowledging Phil Carlton's famous quote, there's only two hard things in computer science, cash invalidation and naming things. So here's an example of some implicit code where this becomes very clear. The names are vague. We're parsing data. What data? What are we parsing out of the data? Who knows? Where is this SEP coming from? I don't see it defined. Maybe it's coming from that star import. But if I have more than one import, then that gets a bit foggy. And what is this even doing? I have no idea. Parsing data out of some file, I guess. Here's the same code simply rewritten to be explicit. I've changed none of the implementation. So the names now reflect the purpose. I'm parsing a name out of a line. Those lines are coming from a list. I'm putting into a list called speakers. Okay, this is coming from file. This is making more sense. And I've clarified that SEP import. That's coming from constants. So that's useful information. I know where that is defined. It's very obvious what this is doing. And if it weren't already, that comment makes it much clearer what the goal is. So focusing a little bit on that import situation, so explicit is better than implicit. And adding to that namespaces are one honking great idea. Let's do more of those. See, the thing is that import star I showed a moment ago is absolutely insidious. First of all, because Python doesn't have any concept of overloaded functions like you may be familiar with from languages like C++ or Java. Instead, it just shadows the previous function. You can't have two functions named spam. The second one is going to hide the first one. And this can have some pretty unexpected side effects and consequences. And working backwards is really hard when you don't even know what module something is defined in. So an example of namespacing being important is here. So we're starting importing from two modules door and window. Who knows what we're importing? It's unclear what we're even really doing. Am I opening the door or closing the door or the window? And open, by the way, fun fact, that's coming from window. I defined it in door and window in complete defiance of could sense. And it happens. And that, of course, completely shadows the built-in open function, which then means, okay, this is going to be surprising if we now try to open a file later on. Or if I want to edit slam, where do I edit that? What module does that come out of? And if we were to import star this module that you're looking at here, then all of these problems would just replicate up to the next level. So this is bad. Let's fix that. No more star imports. And as a result, we no longer have shadowing. We're using explicit namespaces, door.open. Okay, so I could use the built-in open function just fine now. It's also clear what I'm doing. I'm opening a door. I'm slamming a door. Intent is much more obvious. And it's clear where this stuff is originating from as well. So if I want to edit slam, I go into door. So on this same point, in the face of ambiguity, refuse the temptation to guest. So when you come up against code that is not overly explicit, you need to insist on knowing what's happening. Abstractions are not there to save us thinking. They're there to save us typing. You still need to understand when a linkless is being used versus an array, because that's going to have implications on the performance of your sorting algorithms. Or even whether I use the built-in sorting algorithm, what algorithm does it use? You know, how does that function you're relying on actually operate behind the scenes? You should have some understanding of this because it's critical to the debugging process and to writing good solid maintainable code. Guessing what's going to happen is just going to lead to bugs and misunderstandings. If it's not your code you're dealing with, you're dealing with a library, you're dealing with Python itself, which has unfortunately some surprises in it, read the documentation. If the documentation is sparse, read the code. Find out what's actually going on. Abstractions are there to save us typing, not to save us thinking. But if it's your ambiguity, if it's your team's ambiguity, just fix it. Explicit is better than implicit. Okay, so Python code is readable, obvious, beautiful, and explicit. Ideally it's also simple. We write code to be maintainable, not to be clever. And I think you're going to see an example of this here. You got that voice in your head that says, well, a real programmer, don't you hate that term? A real programmer wouldn't use an if statement and you wind up spending all this time looking up some sort of advanced way of handling what an if statement would have done perfectly well doing. And I guarantee almost every person in this audience has had a moment like that sooner or later. I know I have many times. But you have to understand it takes more skill to write simple maintainable code than to write clever code. So let the simplicity be your show of skill. It's hard to read clever code. It's hard to understand it. Ergo, clever. The basics are your friends. So here's some complex code. This isn't necessarily bad, but you see I tried to make this dry by taking this input logic and putting into its own function. But I'm only using it once. So this is dry nearing absolutely arid. This is terrible. Because now I have to add this extra level of complexity. I have to do this sys exit call, which works. But what if there's some unexpected side effects of sys exit that I'm not thinking about? So this is adding complexity that while it's okay to ship this, it doesn't need to be there. I can just stick with the classics, the infinite while loop with a break statement. This is easy to read, easy to maintain. Stick this in my main function and I'm good to go. So the simple solutions are sometimes the best. In fact, if you have a simple solution, it's probably better to do that than a complex one. But complex is better than complicated. While simple is best, it isn't always possible to go for simple. And you don't want to confuse elegance with obfuscation. I don't know where I picked this up somewhere on Twitter, but I loved it. Elegance is not obfuscation. Complex code still needs to be readable, beautiful, and obvious. It might take you more time to read it, but it should not take more effort to read it as long as it's pythonic. So this is bad code. This is complicated code. Once again, this is kind of like writing C code in Python. There's too many steps here. First of all, I'm not even going to support your intelligence by breaking this down. The logic is obfuscated. I'm completely ignoring all of the wonderful syntax that Python offers to simplify this. This is just plain nasty. Okay. So what if I wrote code for finding greatest common factor and I made it just complex but not complicated? What would that look like? Much better. Tighten the logic up first and foremost. It's very clear what this is doing. You don't have to struggle to understand this. It just takes more time to read it. And I'm utilizing the syntactic sugar. I've got a set comprehension. I've got a conditional in this comprehension. So this is much cleaner and it works just as well. So, and again, you can get set comprehensions, list comprehensions, to get away from you. Do be careful of them. But this is a good case of a nice, clean set comprehension in action. This is good complex code. There's no simpler way to do this. That I know of. So Python code is simple or it's complex and it is obvious. So with those in mind, if the implementation is hard to explain, it's a bad idea. That is, if you are having a hard time explaining the code, it's not obvious and it's not simple. It's probably not even complex. At that point, it's getting towards complicated. The problem with cleverness is basically that it's exclusionary. That's the poison. Because it feels clever because you're part of a select set that can understand this. You have the prowess to understand what is going on here. And that is just, that's just terrible. So cleverness is exclusionary. Don't be clever. You want to have excluding yourself sooner or later. Because in six months, you're not going to remember what that clever code did. But there's an important corollary here. If the implementation is easy to explain, it may be a good idea. Because while all Python code is easy to explain, not all easy to explain code is actually good or Pythonic. And just as all Python code is obvious, but not all obvious code is Pythonic. This is kind of along the same lines as all snakes or reptiles, but not all reptiles are snakes. So your code should be easy to explain, but it needs to fulfill the other criteria as well. So are you still with me? Pythonic code is readable, beautiful, obvious, explicit and simple or at least complex. To that aim, Pythonic code is flat. Why is that? Because it's hard to read deep nesting, especially in production code. We can't read white space. And when it gets deep and complex, it's really easy to make mistakes and get it wrong. Is this in the outer else or the inner if? By the way, this only applies to code, not to your data, not to your folder structures in your collections. This is for code. This is overly nested code here. I don't need to say much about this here, but this is really prone to indentation errors. It's hard to reason about what's going on in a multi-nested for loop. And the line limits are now getting painful. True sad story, I worked with the Python developer who switched to 120 line limits and two space indentation because he was nesting all the time. That's if you need to do that, you're nesting too much. So just keep it flat. I'm, instead of this multiple nesting thing, I'm using intertools product. Intertools is a great built-in library. Please use it. It's very helpful. This is much easier to reason about. Just a quick glance at the documentation. And the line limits are not a problem. We have one level of indentation. Very unlikely to mess that up. Meanwhile, sparse is better than dense. Skilled musician or composer will tell you the single most important symbol in music is the rest. As Beethoven says, the music is not in the notes, but in the silence between. The silence defines rhythm. It defines dynamics and one note for the next. Without it, music is just a cacophony. The same is true of code. White space gives us a chance to pause and consider what we've read. It allows us to organize the code logically. And again, while one liners make us feel clever, don't be clever. It sacrifices readability. Do I even need to say anything? This is obviously pretty dense. This is hard to read, even though it's decent pythonic code. And the white spacing is just terrible. So let's fix that. Same code. I haven't changed a single character in this code except the white space. This is much easier to parse. It's broken down into logical chunks. And it's okay that it's a bit longer. Vertical space is there to use. A side note, if anyone's thinking, that's not pythonic, you could have just reversed the spring. Hey, I optimized for time and space complexity on this one. So this is pythonic for my goal. It's for checking for a palindrome. I'm going to get through this here as quickly as I can, then I don't have any questions yet. If anyone does have any questions, put it in there. If I don't get any of those, I'll probably go towards them. I'll probably use a little more of my time. So the term exception is meaningful. Errors should never pass silently. An error is an exceptional state. It means we've left the happy path. And we're now into a territory where we need intelligent intervention to resolve it correctly. So if there is no intelligent intervention, it's best to just crash the program at that point. An error caught at its origin is always going to be easier to debug than errors created when an exceptional state moves forward blindly. And I like how Python developer Mike Pernott calls this the diaper anti-pattern whenever you have this catch-all because you're catching all of the errors and you're silencing them all, the exact opposite at this point of the ZEM. Mike says, in the harsh and unforgiving real world, the source may be tens, hundreds, or even thousands of lines away, buried under umpteen layers of abstractions in a different module, or worse, in some third-party library. And when it fails, just as diapers fail, it'll fail in the small hours of the night when we would rather be sleeping. And someone important will make an awful fuss about it. It will not be fun. So all that to say, you know, don't silence errors. Just, you know, don't let them pass silently. Let them explode where they're happening unless explicitly silenced. It's okay to explicitly silenced. It's okay to explicitly silence an error. But only if you've already applied intelligent intervention or you've otherwise resolved the exceptional state. So if you already know, okay, I don't have to worry about key errors here because I'm just going to return none, that's fine. Silence that error and move on. But if there's an unexpected error there, perhaps you get a runtime error, which if you're getting that on a dictionary read, you got bigger problems than errors. But you need to be specific. Silence only the error you've resolved and let everything else continue to blow up at the source just because it works on your machine and you pass the test does not mean that it's actually going to be safe and safe. So be very aware of that. Explicit is better than implicit. Okay, so these next two aphorisms really aren't so much about Python code as about Python coding. And the biggest lie we tell ourselves, I think is I will do it later. So Tim says, no, now is better than never. Because when you say I'll do it later, you probably won't. And the longer you wait, the more you're going to have to refactor your code to pull it off. So the important thing is to do do whatever you're thinking about while you're thinking about it, while the idea is fresh in your mind, just implement it, or at least set yourself up to be able to easily implement it later. A really good example of this is at my software company. We are building a game and we have some features who want to add in a later version. But we don't want to add them right now. We're trying to limit our feature set for 1.0. So we simply left spaces for us in the code and we structured certain parts of the code to allow us to do these things down the road. We aren't doing them right now, but we have the space to do them. But important and perhaps confusing corollary, although never is often better than right now. Isn't this a contradiction to now it's better than ever? No. Because if you have a live we tell ourselves, it'll only take a minute. Yeah, right. If you have to drop everything to add a bell, a whistle, or a gong, it's just not worth adding at all. So this is all about priorities. You need to ask yourself, is it really needed or are we just piling on needless features? Can't remember who coined this phrase, but someone said that all software will naturally evolve until it is capable of sending email. So I want to be very careful not to let unnecessary features creep in and push out more important things. Special cases aren't special enough to break the rules, Tim says about this whole zen. But my code is different, you say. Okay, great. But that's not actually an excuse because new code is really the only code worth writing. It may be a similar goal, but your code is new, hopefully your approach is new. So if novelty were an excuse, there would be no point in having a standard at all. But we need a standard in order to make things same. Corollary, although practicality beats purity. Because you have to remember, the aim of the zen or any good standard is to protect and enforce readability, maintainability, stability, and performance. And if the rules are detracting from these goals, we may be justified in breaking the rules. So you can break the rules, but you have to have an objective reason to do so, and that reason can only come from the end. Do you need to break a rule in order to keep this code stable, perhaps, or maintainable? Okay, then go ahead and break the rule. But if the only reason you're breaking the rule, you have to think carefully about this. The reason you're breaking the rule is because of how you chose to implement your code. What needs to change is not the rule, but your implementation. So make sure you are only getting your exceptions from the end and not the means. Okay, so on the label, I said 20 aphorisms, but the zen only has 19. Like I said, don't worry, this is not an off by one error, because, as Tim said in his original post, 20, counting the one I'm leaving for Guido to fill in. He left a spot for the benevolent dictator for life to add something, but Guido never did. And perhaps this is just as well, because Python really isn't the work of just one person, it's the work of the entire community. It's always changing and growing and evolving. Just imagine what EuroPython 2030 is going to look like. It's going to be phenomenal. There's going to be ideas here that are just completely new to us, and it's going to make all the trendy and new and fresh stuff from this conference feel really dusty and outdated. The zen of Python fits neatly inside the principle of Python code, but you really can't contain Pythonic into any one document. There's a certain flavor to it that the zen somewhat approximates, but can't fully encapsulate. Pythonic is just something you learn from experience. It's always being debated, parsed, rehashed, reworked, challenged, improved, but interestingly, since 1999, the zen has remained completely unchanged. Pythonic code is always readable, obvious, beautiful, explicit, and simple or complex. We just find different ways of doing these things and better ways of doing these things. So I really hope by explaining the zen of Python to you, I've given you some practical insights in how you can improve your code using this rather infamously snarky little poem. So thank you very much. So I do have one question here, and if anyone else wants to chat with me after this, I'll be in the talk, Writing Zen Like Python channel over on Discord, and I'll be happy to chat with you all there. So I have one question from Martin. He says, what's your opinion on code formatters like black that made code universally readable but in some cases, ugly? I would say, well, like with most things, it just depends on what you're doing. Personally, I like black. That's my own take on it. I rather like it because it generally makes my code more readable and not less. But if you are consistently discovering that your code is not being improved by black, then this is why we have other tools. This is why we have Pyflakes or not Pyflakes. Well, we have Peppate and we have various other tools that can either names are escaping me right now that you can customize to meet your needs more specifically. So if black doesn't work for you, just find something else, or you can go the old fashioned route and just memorize Peppate, actually not hard to get used to once you've been working with it for a little while. And then you can just apply it yourself and not worry about your auto-formatter and its opinions on things. So yeah, I'm actually finishing a little bit early here, which is okay. That's awesome. So stay tuned because coming up next, we have our keynote speaker, Jessica McKellar. And I'm actually going to turn things over at this point. To the next session manager. So it's been awesome seeing y'all here and I will answer any other questions you all have over in Discord.