 All right, my name is Rory Gagan. I'm Jean-Philippe Kissier. We want to give a presentation on, go figure, design patterns in Python. So this is going to be a really, really active live demo type of stuff. So go ahead. All right, so what are design patterns? For over 50 years, of course, all you know code was hard and always hard to write and maintain. Almost 20 years ago, some guys published a book of design patterns which they called the Gang of Force. You all know of it. You all saw it in college and everything. What are design patterns? Well, they are a small, reusable piece of code to solutions, general reusable solutions to problems that we have lots of times when we're developing. Having design patterns allows us to make, of course, testing, unit testing easier because we have a smaller piece of codes which are going to be easier to test. And this makes, of course, maintenance of code to be really, really easier. So thanks for the question. Why Python? I had a friend, I'm sorry, a colleague in a previous lifetime who said, Python doesn't need design patterns because we have met a programming, right? You know, like I've read parts of design patterns and you go through it and you're like, all right, if you're writing in C++, you're writing in Java, you're dealing with a static type system, this is really handy because it lets you do a lot of complicated stuff in a very clear and detailed way. I find for Python, a lot of design patterns are sort of not overkill but too basic, right? To where, oh, why would I do all this when I could just pass a function around? And it does wind up being like that. But to say Python doesn't need design patterns, I mean, I deal with bad Python code. Most of it is written by me day in, day out, right? I mean, it is a classic beginner problem where you get a program of a certain size and you can't make heads and tails. And this is where I think the design patterns we're going to show you come in, where it's not a question of like this biblical come down with two tablets of, we're going to write our program with this, with this is the design pattern and it's going to be head to tails. I've got this certain problem, right? I start recognizing that I'm falling into this trap. I start noticing this pattern. Well, maybe if I design my software this way, it'll be a lot easier to write, all right? So first off, we're going to be talking a bit about iterators. You all know about our magnificent yield keyword in Python, which allows us to have quickly a iterator. What an iterator is, it's going to be, when you're going to be thinking about like it says sequence, stream, record set, filtering or pagination, it will allow you to read a piece of data and only allocate this data and use it. So if you're reading a file, for example, instead of putting all the file in memory, you're going to be reading first lines or number of bytes each other time. And by using the yield keyword, you're going to be able to return a generator, which will be used in Python to, of course, iterate over it. One of the cool thing we feel is you can also have a cascade of iterators. Think of it a bit like layers of onions where like you can see at the bottom, you can like put iterators inside of other iterators and they will yeah, so they're going to be using each one for each piece of data. So in this example, you're going to have one, two, and three, which is a list. Yeah, it's not a, you have a list, but the first element is going to be one. It's going to be written, it's going to be multiplied by two. So you're going to have two. And it's going to be applied on the second iterator, which is, if n is greater than three, which is false, so it won't be written in it. So in the n, you're going to have a result which is going to be four and six. By separating your logic iterators, it's of course going to be really easier to test and also don't forget the iterator modules that Rory talked about a few months ago has plenty of really amazing functions that are really going to be useful when you're going to be dealing with iterators, yeah. I want to talk about polymorphism over conditions, right? This is not even a design pattern. I think I'm cheating. This is just more like a tip, but this is so useful, you know, if there's something, if there's one way that is still, what now, five years of professional software writing, I can, into three words, polymorphism over conditionals. How can I explain this? Right, just to give you a bit of hints, if you ever get into a situation where you're thinking, all right, I'm using a lot of instance, is instance here. I'm using a lot of ifs of a huge tree, right, or condition. Start thinking about polymorphism and conditionals. These sound like very complex words, but it's actually a very simple technique. I'm going to go to the next slide. Right, this is a classic piece of code, right? My boss comes over to me one day and says, I need to do, I write something that does this, right? And I write a class foo, and it does this, and it does it in three steps. And it does this, and it does this for y, and that's very simple. He comes to me the next week, right, and says, the thing that does this works great, but we just needed to do that in these specific conditions, right? Just, you know, bolt it onto the code, make it really fast, because you don't want to rewrite your code twice. You don't want to copy paste. You say to yourself, all right, if I'm in condition a, which was the original one, I'll do this, if I'm in condition b, which is this once in a blue moon corner case, I'll do that. And you start peppering all your code with these if else's, if LF conditions, and you start wondering yourself, this is really long. Also, do notice, right, I have condition a and condition a here, right? Nothing stops me from making an error at one day, committing a change from condition a, changing it a bit, but forgetting to make the second change up there, right? And suddenly, I added a bug in my code because there is code duplication in this case, right? The simpler solution is this, right? It's a lot more code, already admit that, but it's a bit simpler I find in my conceptualization. So you do original class foo dis, and what does foo dis do? It does this, and then I need another class to do that, and I'll call it foo dat, and foo dat extends foo dis, so the do z part, it'll do it again, but because I know that the do x part is different from my foo dis, well I'm saying foo dis and that a lot, I know it's to change it just here. Now the key here is what's called a constructor method, I guess, I like using other class methods because it's stuck to the class and I'm like, oh, this method creates classes of this type, right? You can have it as a function laying down in the module, you can have it whatever you want, you can have it as a dictionary following some types, but the key here is your conditions appear only once, they appear in this class method when you get instantiated, right? So what this does is I'm calling foo pick a class depending on my conditions, I can pass those as arguments to pick foo class, right? So I can clearly outline what is different from this and that, and then from that class that's been picked, I instantiate an object and do x to y and hopefully do z. So the key here is my conditions are executed once, I'm checked once, once I've instantiated a class, those conditions always hold because I'm never re-executing, right? So if I have an instance of foo dat, it's always this method that's getting called, never dat, right? So debugging becomes a lot simpler because I need just to check that this method works. Next. All right, next one's gonna be strategy pattern. This one is really close to a dependency injection we have in Java without all the magic stuff that goes with it. When we're doing a strategy pattern, we have like a controller which, and we have different strategy that are going to be used by the controller. So when we're gonna be talking about strategy, we're gonna be thinking about, like it says, separations or handlers or like we wanted to say, bring your booze, where you bring your own implementation. Instead of doing some duct typing, with a strategy pattern, it's gonna be a bit easier to test with your unit test because you really isolate your strategy which is going to be implemented. And of course, each strategy is gonna be different but it's gonna be, it's gonna provide a common interface to the controller which is going to be used. So a small example that we have, let's say the CSV module have a writer method and all it takes is an argument to where it's gonna be written. Of course, you don't want to be writing for each different strategy. So the first one is only the started output. The second one is gonna be the file, you open and you write. And the third one is the string buffer. By separating your strategy, each piece is gonna be, it's gonna be implementing what it has to do. And yeah, so the CSV doesn't care what strategy it is given as long as it can execute the, for example, write method that each one is implemented. One of the things to be careful is leaky abstractions. So let's say that you have a flush method in your CSV module but the flush, you don't really have to flush, for example, buffer string. You have to do it for the file and the standard output. So instead of just raising an exception when you do a flush on the third method, this has to be given to the strategy which is gonna be a stub with no op to make sure that you don't have an exception that goes with this. Transaction is where you will have an object and you'll have operations you'll do on that object. And each operation you wanna, first of all, condense into either method or an object itself that'll describe, that'll be self-executing and you wanna catalog them and keep them, right? So what you do is your object doesn't have a state. All it has is a list of operations that's been executed on that state and to get back up to the current state, you go through your list of operations and execute them one after the other. This is a lot of hoops and a lot of tiger tamers for a couple of useful reasons. First of all, you keep this audit log, right? You can tell what has been executed on this object and if you keep tracks of timestamps and maybe users, you can use this to debug, to figure out, all right, I had this object. It was in state this last Monday and nowadays in the databases in state Y, what happened? Oh, I have this transaction log. So whenever you're thinking you hear, oh, I need to keep an audit log, I need to implement undo, redo, right? What's undo? Remove a transaction from the log. What's redo? Readd it back again if it didn't have to be. Merging data a lot easier if you have this log of transactions that you can fight one by one or a controlling versions. So if you wanna go to the next slide. Here you have a very simple bank account which is the classic transaction type of thing that you do not keep like a bank account of somebody is not just a value, it's incoming money and outgoing money, right? So here we keep this log as I have this object, this list calls trans, which is just an empty list right away. Initially, your bank account is empty when you create it and every time you deposit, you add an amount and every time you withdraw, every time you deposit, you record I deposited such an amount and every time you withdraw, I record I have a withdrawal of this amount and to get the current balance, it's not a fixed number, right? So it's not optimized to find my current balance. I have to go through the list of transactions and sum it up to figure out what my current balance is. However, this has two useful benefits. First of all, you can keep, if you start recording timestamps and users, you can start, you can tell people when have they withdrawal, when have they deposited. If you make errors, you can go backwards because you just truncate that transaction log the trans list and remove the wrong transactions and arguably, depending on your underlying data structures and whatnot, this is very read slow because you have to recalculate the sum, but you can cash stuff to try and make it faster but it's arguably right fast because all you're doing is appending too long. So if you have a database, it goes really fast. All right, next one's gonna, we're gonna be talking about the PubSub Pattern, Publish Subcribe. This one is a derivative of the Observer Pattern, which you see most in GUI application with MVC, so the Model View Controllers, where your view is gonna be listening to your controller when your data change notifies your view. The difference with the PubSub Pattern is we have a central broker which is managing the listeners and the emitters, so the subscribers and the publisher. With a PubSub Pattern, you're gonna be thinking about like, yeah, message queue, subscribe, or even callbacks. With the PubSub Pattern, it's, as you can see here, it's easier, this is on your left, this is the Observer Pattern, where you have your listeners and your emitters. So those three listeners are connected to these emitters. If this one has to be disconnected, you have to notify the three listeners that you're gone. And with the PubSub Pattern, because of a central broker, if let's say this one goes out, only the broker has to be notified, and the listeners don't really care to who they are connected. So it's easy to swap up listener or emitter. We have a naive implementation here, where your broker, you're gonna have your list of subscribers and publishers. Each are going to be subscribed by either the publisher or the subscriber. And when the publisher wants to publish a message, so all it does is it iterates over those who are subscribed, and you're gonna push the message. Of course, this is, like I said, it's naive, so usually you're gonna want to have maybe some identification to your subscriber and your publisher so that your broker can filtrate the message. So it doesn't have to give it to all the subscribers. I wanna talk about the state pattern. I put this pattern in just so I can say automaton in front of people, because I really like that word. I don't know if you all remember your theoretical computer science class, I really liked it, where you learned about finite automaton, right? So what this is, when you're thinking about, whenever you deal with problem dealing with state, with workflows, it's very useful. If you have different modes reacting to the same sort of interface or the same thing, but in different modes you have to spread out, you should start thinking about the state pattern. So the key of the state pattern is before you start implementing it, you sit down with a piece of paper and you name, you list all your states and you list all the potential inputs. So not inputs like a mouse, like a keyboard input, like my user clicked on this button, my, we got this string in on this port. So specific inputs that are useful to your application, they usually represent actions or operations, right? And the point is you draw all your states and you draw arrows between them representing inputs, okay? So in this case, this represents, I stole it from Wikipedia, it represents a turnstile, right? So your turnstile, all automaton have a start state, in this case represented by a black dot, you start off locked. The turnstile is locked, right? If you push the locked turnstile, it stays locked, right? If you put a coin in the turnstile, boop, it goes unlocked, right? If you put another coin and the turnstile is nice, it'll return you the coin, but it'll still stay in the unlocked state. And if you go through the turnstile, it'll go back to locked, right? You want deterministic finite automatons over non-deterministic finite automatons. What the heck does that mean? It means that your system here, so your series of states and inputs and actions, right? So the arrows, all potential states need to react to all potential inputs. You don't want this gap of, oh, you can never be in this state. Who would give that input to this state? Trust me, a user will figure out how, and when that happens, you'll get an ugly error message. So remember, in an automaton, error can be a state. You can have the error state. If you have a bad input to a certain state, in this case, it goes back to the state, or it can go to an error state, and the error state is send an email to the administrator, pop up an error box to the user, stating what you tried to do here is not allowed, or revert them to an FAQ or something, right? But the important thing is, every state should respond to all potential inputs. If you go to the next slide. Ooh, if you could read that. This is the implementation. So there's a class called locked, which is locked off the top there, and unlocked, right? And the way I like to implement it usually is that the state passes back itself an implementation of your of its current state, if you stay in that state, or the next class onwards, right? So all you're doing is you're instantiating state and doing operations onto it as you go along. So if you look at locked, if you pass, if you push unlocked, it returns self, because you're in the same state. If you put a coin, which is an input, it returns an instance of unlocked, and you just move on, and you moved on to the next state, right? And to see the current states, to figure out what state am I currently in the system, it's just to name the class, and there's no way of confusing two states, or of thinking, oh, I thought I was in this state, but the code is reacting as if it was in another state. And it's a relatively simple and very easy to test, because all your states are one class, and you just hit all the states with their inputs, and you're expected operation on that state. And that's basically it. I hope this was good. This is just a very short zoology of very few design patterns, which I found useful in Python. So thank you very much.