 Let me introduce myself anyway. My name is Pablo Fedache. I work for Skazka. Do you know it? Do you use it? You should use it. Or even better, you should join it. We do pretty good stuff and we are hiding, so you know. But I'm not here to speak about myself or my company. I'm here to speak about the creators and mystified. And what we will try to do is try to understand what the ad symbol, what this syntactic sugar does. And to do that, actually, we're not going to talk about the creators. We're going to talk about namespaces and scopes. Because if we really want to understand the creators, we have to understand namespaces and scopes in Python. Because if you understand them, you will really understand how a decorator works. How many of you have implemented a decorator? Raise your hand. Whoa, a lot. And you understand what are you doing when you implement it? Or you just stack overflow, copy, paste, rename, replace something and cross the fingers and it works. So let's try to understand why it works. So at the end what we will do is we will implement our own handcrafted decorator. We will try to understand all the basics behind the creators and then we will implement our own decorator in a really, really simple process. You will see that at the end it's really easy, really simple to understand. But before proceeding with the cool stuff, let me show you something that we will use during the talk. In first place, this is a really simple software catch. I have an order at Dick where I store keys and then I retrieve the keys. Do you know order at Dick? Raise your hand. For the rest of you, order at Dick is a dictionary which also keeps the order in which you inserted the keys inside the dictionary. Why do I use an order at Dick? Because I can limit the size. I want to limit the size of this catch. It's really simple catch, but I want to limit the size. So you can see the pop item last equal false here. It means that Python automatically removes the oldest, the first inserted item when the dictionary is full, or when the dictionary reaches the size that they want to limit. Do you understand this catch? Yes? Pretty simple, right? Don't use it. Never use it. Please, never. This is a crappy catch. And it does not do what we would like to do usually. But for the sake of the example, we will use it. But usually you should use funtals.lrucatch since Python 3.2 or 3.3, I don't remember. But you can find the code to implement it actually. I think it's in PyPy, in PyPy, for 2.7 and 2.6 also. Use that if you need a software catch. More useful stuff for the talk. Factorial and Fionacci, do you know them? Do you remember them? Yeah? Implemented in recursive way so the function calls itself several times. In the case of factorial, only one recursive call. But in the case of Fionacci, two recursive calls. So you can understand that Fionacci is way, way more complex. Right? Cool. Is it right? Yes? No? Are we awake? However, there is one thing. Or more than one thing. What about this? Did you ever try to figure out how is it possible that you are accessing catch there? How many of you come from maybe Java or the net? Really? You're kidding. For the five of Java or the net, is it worth to do this? Access maybe here, where is my mouse here? Access maybe here, the element declared outside the function. Is it worth or not? What do you think? For the ones in Python, maybe it's like normal. Yeah, I do it every day and there is no problem. But there is something there that lets you access that name. Did you ever try to think about it? What about this? How is it possible that the function calls itself? How do you know that you are going to have this name when you are being created? I'm a function and I'm created and I want to call myself. How do I do it? There is a magic there. Let's see another example. Even simpler. We can import paths from one module. Or we can import paths from another module and there is no clash there. Well, if you do both in the same place, one overrides the other, because they have the same name. But they are declaring different modules and they don't clash. How does it work? What's happening here? Or even I can have a function with again path as one of the arguments. And again, no clash, no problem here. Compiler doesn't come, well, compiler. Interpreter doesn't come play. It works, right? It's something natural in Python because you use it and it works this way and you program and you see it and you think, well, this is the way it works, right? Why? Why is the way it works? Why is it happening? Let me introduce namespaces. Woohoo! Come on. Spaces, oops, sorry. That's it, that's it! Come on, guys! Thank you. What's a namespace? A namespace is a mapping for names from names to objects. Do you know in Python everything is a pointer? So a name is a pointer. It's a name you are giving to the pointer to an object. And that's why, well, no. Let me explain something else. Some examples of namespaces. The built-in names functions acceptance. We implement something like try accept system error in out exception, whatever. How is it working? What is the name declared? How is it possible that you are getting that without importing? Because that name is an namespace that you can access always. We'll see how this always works, how this access works, okay? But that's the built-ins. There are others. There is the global names in every module, like the cache we saw. Cache is a global name in a module or a top-level attribute, if you want to call it this way, because at the end it's only an attribute inside a module. Globals, what we call globals in Python, maybe it's a misleading name because in our language it's a global. It's really a global inside all your codes, inside all your execution, but here that's not happening. A global is only a top-level attribute in a module and you can only access from inside the module. So it's just to say top-level attribute, I think, or more accurate. There are also local names in a function invocation. The cache argument that I showed you. The cache argument of the function is a local name inside the function. What's this inside? We'll see it. There is also the last one, which is maybe the weirdest one, but the thing is when the interpreter runs, it also creates some top-level names that are accessible for you. They are not the built-ins, but they are there and are relative to that execution of the interpreter. But we cannot make this one. So there is no relation between names in different namespaces. That's why we can have path in one module and path in another module because they are in different namespaces and that's why they don't clash. If you use the same name in the same namespace, what would happen is that you are replacing the pointer to the object. You are assigning a new object to that name, right? Another thing, namespaces are created and deleted at different moments and have different lifetimes. Let's see more examples. The namespace is created when the path interpreter starts and it's never deleted until you close interpreter, of course. Right? What about the module global namespace? It's created when you import... No Wi-Fi, sorry. It's created this way you pay more attention. As I was saying, the top-level attributes of a module is created for first-time the namespace when you import the module for first-time, also. And then it's there because Python uses it. If you import the same module from different places in your code, in your application, it's only... Let's say that the file is only read once and Python stores and keeps it inside the memory. So the namespace is created there the first time and that's it. Unless you do something like reload. Do you know reload? And that's the way to destroy the namespace and create it again. That's the only way you have to do it. Otherwise, the namespace will remain there together with the imported module. However, function local namespace is created every time you call the function. Actually, it's there, but it's filled every time you call the function. And why is that? What does it mean that it's filled? Well, it's because the scopes. What's a scope? A scope is an extra region of a program where namespace is directly accessible. Do you get it? So actually, it's a scope which tells you that when you are in a function, you can access the global namespace of the module where the function is declared. That's the scope of the function. And the scope is determined a static alley. It means that the first time that Python interprets that function takes the code you wrote and interprets it, Python figures out what are the accessible namespaces. What is the scope of the function? And at that moment, only at that moment is when Python computes all the stuff accessible from the function. If you have nested functions, you declare a function inside another function, inside another function inside a module, then you have different namespaces and all of them are accessible. They are used dynamically, however. Depending on where you are, or what you are executing at that moment, you have a different scope. A different scope is applied. So a different set of namespaces are accessible at every moment. Yeah, that's obvious. At the end, it's obvious, right? This is how it works. You call a function, then you have access to the locals plus all the globals. When you go out of the function, you have access to the locals of the function anymore. That's a scope. At least three nested scopes whose namespace is directly accessible. Let's see them. The innermost scope contains local names. As I said, when you are inside a function, the scope is where you can access inside the function. Then you have all the enclosing functions. Enclosing when declaring. Remember, declaring, not calling. If you call a function, and that function calls another function, you access the locals of the calling functions. That's obvious. You only access what you declare. Okay, so this only works for nested functions. Do you remember how we implement a decorator with a nested function, right? That's why I'm explaining this. After the function and all the enclosing functions, what we have is the current module global names. Current module is the module again where the function is declared, not the module from where you are calling. Okay, this is really important. A function only access the wrongly called globals inside the module where the function is declared. Okay, we have the buildings, and that's the outer most accessible namespace. Everything clear so far? Yeah? Yeah? Cool. So names asserts in nested scopes from inside out, from the inner most to the outer most. In the example of the function with an argument called path, when you send a function, you get to retrieve path, you get that local name. But if in the same module you have another function which does not have an argument called path, then Python will look in the outer namespaces. And if you were importing OS.path, then you would get that path. Or it would fail. If Python goes from the inner most to the outer most namespace, trying to get the name and it doesn't find the name, then it's when you get an attribute error. Right? From one to the other. Remember this also. The access is only for the outer namespaces. You can only change the names in your local namespace except the globals if you declare them as global. Did you know the global keyword? If you don't, you should come to the training tomorrow. Let's see a bit of codes. Yeah! This is it. In green we have the inner most scope. With the local names. All the names declared there are the names that you can access when you are inside the function as locals. So you can modify them. Then you have the globals which is the next namespace. And then you have the buildings. What happens with the names here? You cannot access them from this module. This is a module. This is a file. You cannot access the stuff in your command line or in any other file, right? So you could see it as a kind of tree. The upper most, the outer most, is the building names. That's the upper most namespace. And then in the leaves you have the locals namespaces for every function. Is it clear? Any questions? So far? Let's take this example. Let's spend one minute trying to understand what it does. I have a function which does something, forget this something, and returns an attribute. What's this attribute? Actually this attribute is another function which I create here. This is a nested function declaration. So I return the function. What does this function? It does a power, right? With x and y. Where is y? Yeah, we will see it with more detail. How it works? If I call it with 4 and I get the resulting function, I call after was a function providing x as 3. Do you see how it works? Does it have sense? So where is y defined? Let's see the scopes. We have in green again the innermost scope. In red we have an intermediate scope, the enclosing function. And in blue we have the globals. And in yellow we would have here also the buildings. So where is this namespace? What happened with it? We have y there declared, right? y is declared here in this namespace with a given value. 4 in the example. So the thing is that we have a closure. This is called closure. Did you know closures? Raise your hand. Come on, what time is playing then? Correct me please if I'm wrong. So you can see the closure here. This is the enclosing function namespace, but only a small portion of the enclosing function namespace. Why? Because as I said, when you declare the function and Python interprets it, Python computes the scope of the function, and Python determines that the local, the innermost function will access something from the intermediate namespace. And as you will need that thing accessible always, Python creates this closure. A kind of temporal namespace where that name is stored. And that name y, letter y, pointing to an integer with value 4 is stored there. And it remains there while you are using the function here. So y raised to 4 is a function here, a valid function. We have that closure there stored. And as you can see, actually the closure is a hidden attribute inside the own function. We have also the globals. There is another funny thing in the example. And it's the fact that as I said, I declare x here, I also have a different x outside, and there is no problem. I'm not replacing one with the other. And we can see here how we have in the globals the x. Everything clear? So in better words, a closure is a reference to each of the non-local variables of the function. And again the same colors. This is the enclosing, this is the enclosing function is called the closure. So far, well, decorators, come on, what's this? I'm fooling you, no, I'm sorry. We will do a decorator, I promise you. We will manually apply a decorator. Let's go back to the functions. Remember factorial and Fibonacci? Factorial is pretty fast, right? For 35, it takes 0.00007. That's fast. However, check Fibonacci. For 35. Come on, it's almost seven seconds. 1000 times slower. Amazing, right? Such a small difference in the implementation. This is the recursive calls graph for Fibonacci of 5. And check the number of calls that we are doing. It's amazing, imagine for 35. I will need the whole walls of the whole room. So, do you remember we have a cache? Yeah? Do you know what's memoization? Let's apply it. Has sense, right? Do you know what's memoization? Raise your hand. Cool. Memoization is a technique to store the result of a call to a function with the arguments. This way, you can retrieve the same result. If you know that the call is deterministic and you will get the same result, you can use the cache result, the store result, instead of calling the function and spending all the execution time of the function. That's memoization. So, it works. The changes are... If I find my mouse, the changes are here. First thing, if it's not the base case, I check inside the cache. If I have the value stored, I use it. Otherwise, I do the recursive call and then I store the value and write on it. So, check now. It's as fast as a factorial. That's a 1,000 times improvement. It's amazing, right? We can compete even with an edge of 100 and it takes also nothing, almost nothing. However, don't repeat yourself. Come on. Why do we want to apply for factorial exactly the same trick? Do we have to implement factorial again? That's ugly. Come on. We had the function working fine and now we change its implementation for catching. Do we need two different functions? One if I won't catch, one if I don't want. No. No. We will do a magic trick. Pay attention. Yeah, I'm a magician. I mentioned in the introduction, I'm sorry. This is a trick. The function remains unchanged, completely unchanged. You see it? I key my function. I store an alternative name to retrieve my function because at the end a function is an object also. So, I have the function object somewhere in Python memory with a pointer called Fibonacci and I take another pointer called Fibonacci and then I change Fibonacci. And this is a question. This is a trick. Which function is called here? I'm fooling the Fibonacci function. The own function believes that it's calling itself and it's false. It's not happening anymore. Let's see it with the colors and just in case let's repeat the trick in a slow motion. First thing, I create the function. This is what happened. I have a global name called Fibonacci which points to an object, function object, which has this code. Then I create the real Fibonacci name, real field which points to the same object. Just a pointer. Just an alternative name to access the same object. And then here comes a trick. Check the colors, the green and the red one. Now I create another Fibonacci. What happens when I create a new function called Fibonacci? I'm replacing the name in the globals. That's why I have it in green. You can check the ID. And now I have two function objects. The second one calls the alternative name. I always swap them. That's a trick. Part of the trick is that the original function does not know it. Hacker and magician. Don't forget it. But let's make it even more reusable. Even more. Let's make it to work with any function. Kind of any. This is a star because it will work only for functions receiving one argument. That's the any. But it will work for any other function. And we create a function generator. Again, we saw that with the powers. We do a similar trick. We create a function which creates a nested function and returns that function to let you use it. Why? Because we have to provide the function to memoize, the function to remember to catch the calls and reuse the result. Because here the only thing that we do is we call the catch with the argument. In case we don't retrieve the value we call the function to memoize we store the value and we return the value. Check only the code of the nested function. And then it works. Do you get it? Is it clear? Yeah? No? And it works for Fibonacci and it works for factorial. Right? 250, come on, 250. Without memoization it would take all the conference. And finally a long last decorator. Woohoo! Let's see decorator. Wait, wait, wait, wait, wait, wait, wait, wait. We spoke about the add symbol. What is the add symbol? Pay attention. It's here. It's here. We have it. We have it. You see it? That's all the thing the add symbol does. Compare both codes. It's exactly the same. So you can use the decorator manually as I'm doing here. And the add symbol is so... There's nothing. Come on. Fair for coming and in case you have questions the slides are there in the speaker deck and I guess I will give some to the organization or whatever. Let me congratulate the organization for yesterday's party. That's great. Thank you. Hope you enjoyed. Tomorrow I'm giving a training. We will see this. You will do it yourself. You will experience it yourself on your own. And later we will see how to implement different types of decorators. We saw a really simple decorator. You can implement decorators that you can customize its behavior decorators which store statues and so on. Tomorrow at 11. So if you won, you are free to come. Questions, comments. Does anybody have questions? Really no questions? Yeah. You need decorators. That's why you don't have questions, right? Hi. I have a question. Thanks for the talk first of all. I would like to ask you basically replace names and namespaces and so on and you showed the slide what a good hacker I am. So I would just like to have a comment from you about using this technique in production code. But this technique is what you do every time you use a decorator? Not about decorators about changing the names, replacing names like the first couple slides you showed about Fibonacci and redefining the name. Yeah. No, seriously. This is what happens when you use a decorator. The decorator takes your function, writes it, how we show it, and it works because the function still believes that it's calling itself when it's called aggressively. It's really what's happening every time you use a decorator. So every time you're using Flask and you want to add a URL and you're doing the streak. Yeah. Okay. Maybe my question was a bit confused. It could look like a hack, but that's a decorator. You are taking the function replacing it or wrapping it with something you could even implement in a decorator which does not call the real function. That would be maybe a hacker. Okay. My question was not about using decorators in production code. Yes. Any other questions? No? Cool. Thank you for the talk. Will you talk about decorators, parameters on your training? Yeah. We will see this was the simplest decorator ever. We will see more advanced decorators. Different types of decorators. We'll experience this ourselves. So the first part of the training is doing kind of this yourself. And the second part is doing more decorators. Different types of decorators implemented to maybe keep an estate inside the decorator or customize the way it behaves. For instance, this decorator here is catching. Why not be able to customize the size of the catch and the time to live? We will learn how to do that. Okay. Thanks. Another question. While I run, will you cover class decorators on the tutorial? What? Will you cover class decorators in the tutorial? Yeah. I don't think we will have time. But, you should use metaclasses. I like moment classes instead of decorating classes. Personally. But we will see how to implement decorators using classes. That's what we will see. I don't think we will have time to cover all the decorator classes. I have a question about using multiple decorators on the same function. That's dangerous. How do you see the trade-offs between the complexity of having that file? That's something that today I didn't mention it, but most probably in the course I would highlight for sure. The thing is, if you have four decorators in a function, it means that the first decorator calls to the next decorator which calls the next decorator which at the end calls your function. So that's expensive. So don't do that, for instance, in your super-fast rest service. Because you are doing four calls for the same. And sometimes you don't really need them. So take care with that. It's maybe the dangerous part of decorators that it's so easy to apply a decorator with that that you forget that actually you have a function calling another function and before calling, usually you do something there, like catching or logging or whatever you are doing. And that takes time. At the end you have to call one to the other, to the other, to the other. Plus all the execution code of each of the functions which are the decorators. And do you have any tips on, for example, if you have legacy code that uses multiple decorators to refactor it, to make it faster? Maybe you could if you always use three decorators together, maybe it has to have only one. The end is, maybe it's not about decorators, but it's maybe about modularity. If you always see it as if you a lot of things that you do with decorators could be replaced with calling something at the beginning of your function. Lots of times could be replaced by the same. But you are always doing the three first lines of your functions are the same calls to all logging and whatever. Maybe you could have single handler for everything together. So it's the same, maybe you could have a single decorator for doing all the stuff together if you always do it together. Does anybody else have a question? Right, thank you Pablo. Thanks for coming.