 And Facundo Batista will be telling us about Python descriptors. There will be some time for questions at the end. So hang on to that, and I'll come around with microphones at the end. I'll leave it to you. Thank you very much. Did you hear me OK? Yes. OK. Welcome. Thanks to be here. Thanks for the organizers of putting together this awesome conference. I will be talking about descriptors. Just trying to everybody understand a little descriptor. Descriptor is a hard topic. Normally, the 50 times that I saw presentations about descriptors until I understood it, it's a lot of theory. And you get out of the presentation saying, OK, I kind of understand the theory, but I don't have a clue how to apply that. So we hooked that this hocking that is the other author of this talk. We decided to do it backwards. So we like games, board games, and role games. And we decided that as we are nerds level two, we not only wanted to play board games, but to create them to design a system where you can create board games. So it's like playing the game, it's like a meta-play. So we wanted not only to create the system, but we wanted to offer other people ways to create board games. And we wanted to offer them a possibility of creating board games in a simple way, in a useful way. So we wanted to do something like this. We wanted to have powers and characters. The powers is where you encapsulate what the powers can do. So for example, you have a strength that is a useful power for a character. And if you have a strength, you can break walls or you can jump a hole, et cetera. If you have magic, you can do an spell, et cetera. See that it's very simple to find the power. It's just a class, it's just what you can do. And the action itself is a method that, of course, according to some value, you have more and less magic, et cetera, you can do it or not. And when you define a character, you just define what powers this character has. So this is very simple written. If you write it like this, it will not work, but we wanted to do this. And we wanted to go from here. So this is how we will use this very simple mechanism. For example, you instantiate a character that is Gimli. Gimli, you can instantiate it with a lot of strength and see how you can check if Gimli can break a wall. You ask for the strength of Gimli and see what the power can do. Also, you can modify very easily the value of that power. See that how I directly modify that power in Gimli. So if I put more power to Gimli, for example, it will be able to break the wall. Or, for example, this is a character that has both powers. So it has some strength and has some magic. And if it's OK, Gandalf can charm a tree, it's OK, but Gandalf cannot fight Saruman. But I can just put more magic in Gandalf. And from that point, Gandalf the White will be able to fight Saruman. The idea is that we wanted to present, we wanted to give the powers in the characters in a very simple way to manipulate, working with them like numbers. So you can get or you can set the powers in the character, but also encapsulating what the power will be able to do in that very same class where you define the power. So it's a very weird way of doing things, but it's very simple. So how we did this? I mean, we didn't do it with magic. I mean, this is not magic. It's just we used the scriptors to make that work. So the idea here is that we will go now a little into the theory of the scriptors, and then we go back to the board game and see how we did apply that theory. So this is last chance. If you want to see the scriptors, you stay. So a descriptor in general is an attribute with binding behavior. One whose attribute access has been overriding by methods in the descriptor protocol. Very, very simple, right? It's very understandable. I mean, you already know how it works. The idea is to see what we really are doing here in more simple words. The idea behind the scriptors is that you take control of some normal Python behavior. Every time you do a set of an attribute in some object, every time you get some attribute from some object, or every time you remove some attribute in some object, with the scriptors, you get the possibility to execute your own code instead of normal Python behavior. So when I do some object.attribute equal 42 in that case, it's not Python just assigning the value 42 to the attribute in this object. But instead, if I'm working with descriptors, some code of mine is being executed in that moment. How is that? You define the descriptor with classes. For a class to be a descriptor, it has to have some special metals. In this case, we have a dunder gate. The dunder gate makes this class a descriptor. This dunder gate will be executed every time you access the attribute. So let's see it in real code. In the upper part, I have the very same class that the last slide, where I have a dunder gate, and that dunder gate just return hello world. Then I define another class that is the class where I use the descriptor, and see that x is a class attribute that is very important because the descriptor magic is activated when you are using it. We are using the descriptor as a class attribute. x is a class attribute that has an instance of the hello world descriptor. Then I instantiate any class, and the moment that I do ac.x, the moment that I'm accessing that attribute, my own code is being executed. So this is the proof that with descriptors, I can get in the middle of the Python execution for this gate. So far, how are you doing? It's totally crazy. Makes sense. I already lost you. It works. You see how this magic happens. So we have another special method for descriptors. We have the dunder set. The dunder set is executed the moment I set the attribute that when the attribute is the class descriptor. This is very similar to before. I have my descriptor that is a normal class, but as it has the dunder set, it's a descriptor. I use that descriptor in a class attribute in another class, and then when I set that attribute, my own code there will be called. See that the, I didn't mention that. For example, in the dunder gate, I receive the instance of the class, and the class that instance belongs. It sometimes uses for some clever tricks. The class there is not normally used. In the set, it's different. In the set, I receive the instance that is AC, and the value that I'm setting. So I also receive this bleh. Okay. Let's see both working at the same time. This is an example code, just to see a descriptor that has both methods, dunder gate and dunder set. See that the dunder gate is the dunder set, what they do. The dunder set receives a value, and just store that value in the instance dictionary. And the dunder gate retrieves that value with a default in case it's not there yet, and just returns a greeting. Let's use it in the second column. I define a new class, hello world 2, that has a class attribute, again a class attribute, that is a grid, with an instance of this healer. I instance hello world 2, and if I access, if I do healer.grid, that dunder gate will be executed, that will return hello unknown, because the instance still doesn't have the who set. The moment I do healer.grid equals something, the dunder set is executed, and in this case, I put that information in the instance, and when I access the attribute again, it just restore it and use it. Okay. Yes or no? Okay, we have two types of descriptors. We have overriding descriptors and non-overriding descriptors. This is where confusion starts to flourish. But don't worry, it's very simple. They are just classifying two groups, because the behavior is a little different. Where the behavior is different, here. In the first column in the left, we have an overriding descriptor. See that we have a class that has both dunder gate and dunder set metals. In this case, I use the descriptor here. When I do c.d, it executes the gate. And when I do c.d equals something, I execute the set. This is very similar to what we saw before. More than very similar, it's the same. In the case of non-overriding descriptors, we do not have a set metal. The difference of not having a set metal is that when I use the descriptor, if I do a c.d, of course it executes the gate. But the moment I do c.d equals something, the attribute d of the instant, c is overwritten by this value. So from this point on, c.d is one to three. It's no longer the descriptor. In this case, I execute this dunder set, and from this point on, c.d is still the descriptor. This is the only difference. You have to be careful when you write your own descriptors, because even if you don't want to put any special behavior in the set, you need to be careful that if you don't specify the under set in your descriptor, it may be overwritten. Of course, it's a behavior that you may want to use. And we will see a very common case where that behavior of non-numbering descriptor is used by you normally. Okay, just for completeness, we have another special method that is dunderdel that, as you may imagine, it's a specified code that is executed when you delete an attribute. So in this case, when you do del ac.ex, you override the normal Python behavior of removing that attribute, but you just execute some code of yours. Okay, one note here, because when I present these examples, you say, oh my God, yes, you can do whatever you want with properties. It's instead of using descriptors manually, yes, you can do, but the point of the talk is to talk about descriptors. So I cannot make the examples too complicated, et cetera. So let's go back to Wizard's Dwarves and see how we use this descriptor theory in the practice. Remember this that we have. The idea of having powers which encapsulate what the power can do and the character defining the powers as class attributes, and then being able to just instantiate the character, try to see if the character can do some stuff asking through the powers that the character has, but also modifying the values of the powers like simple numbers. This of course doesn't work if you just write this because some magic, we need to add some magic here. How we make this work? Okay, we make this work not only using descriptors, but also class decorators. So the idea behind all the magic is that we have this power descriptor. This is the first time I presented descriptor for the Wizard's and Dwarves case. This is the only descriptor that we have. This descriptor that has the under set and under get, when you instantiate it, you get the name of the power and the class of the power. So it can be magic and the class magic for the power you define it. And the only thing that it does is just store in the instant dictionary, very similar to the other example, it just store the instance of the power class that it received with the value that it received. And in the case of the under get, it just retrieves that. How we make to this power descriptor to be used by the other parts of the system? We have two class decorator. We have this at power that gets this very simple power class that you wrote and makes two things. For one on one hand, it registers strength or magic as a power, but also makes that these classes behave like numbers. And then we have another class decorator that is character that grabs the character you wrote and for the class attributes that you specify there, automatically converts them to be descriptors. To be descriptors because there you just put class descriptors. Sorry, there you just put class attribute, but these are not descriptors, so it automatically converts them. Before going into more detail of the code, let's see other cases where the descriptors are used in real life. So Python methods is the most normal case of using descriptors. When you define a method, what is a method really? What is the depth there? It's a function, right? And the function there, where is the finite function in the class? So method is a kind of class attribute and this works like a descriptor. The descriptor does this magic, so when you call food.method12, the special descriptor is executed that calls the function, you define it, adding a self in the front. So the magic of automatically inserting a self when you call in a method is done by descriptors. And these are non-data descriptors. So if you have an instance foo and you do foo.method equal something, you override that name. Remember that we have two types of descriptors and if you had the under gate and under set, the descriptor was executed and if you only had the under gate, when you set the attribute, you lost the original descriptors. Methods works like that. If you set a name or something with the name of the method, you lose the method in an instance. Python works like that. So Django, most of the magic in models declaration of Django is done by descriptors. The very same way that I put the powers in the characters before, you just specify stuff at the class level and it works. How that works? They are descriptors. Did you ever use slots? Slots also work through descriptors with a very detail that is not implemented in Python really but it uses the descriptors API directly from C but it also uses descriptors, the concept. How are we going with the time? Okay. So we have time for the bonus track that is where we get into the class decorator and the detail of the code to make that game board work. Before going into the detail of the code, I wanted to present a little about class decorator because it's not a common topic. Class decorator, I don't know if you ever heard about razorhands if you ever see a class decorator before. Awesome. And razorhands if you saw a normal decorator before. So this is very simple. A class decorator is a function that receives a class and returns and returns a class. It's the same that the function decorator but for classes. It's the same concept. If you have a definition of class foo, whatever, foo is the class we define it. But if we have a class definition with a decorator, foo is not what define it. Foo is the class returned by that decorator which receives the class that we define it and did whatever he wants with it. It's the same than doing foo equal decorator foo. The very same thing that function decorators. So how did we use it in the board game? This is the class decorator that I mentioned before that you just put it in the very simple power class that you define and makes it magic. See what it does. This code is not really simple but we can make it understandable. The power receives a class. The class is this one that we are defining and the power decorator creates another class, a new class with a type function that inherits this very class but also inherits float. And this is how the behavior of a number is inserted to the power. And also registers that class name in a global dictionary that will be used later. Also we have another class decorator that was character that we applied when we define characters. Character, the class decorator, just gets the registered powers and if we have a class attribute in the class that is received that has the same name of a registered power like strength or magic in this example, it replaces it. It replaces it. See that setator? It replaces it with a descriptor that is our power descriptor that is the only descriptor that we use in this system. It replaces it with that power descriptor that holds the name of the power and the class of the power. This is how you are be able to write this very simple code to make the board game without even noticing that descriptors works in the back. Okay, that's all. It wasn't that hard. The concept that I want for you to take here is that descriptors allows you to get into the normal Python behavior when you get an attribute, when you set an attribute, and when you delete an attribute that you have two types of descriptors, those two types of descriptors, the difference is that in one case, when you set an attribute, the code of the descriptor is executed and in the other type of class descriptors, when you set the attribute, the descriptor is removed from the instance overwritten. And also the idea that I want for you to take from here is that it's not deep magic that, okay, maybe it's useful for me to understand because I may hit some code somewhere and I need to understand it, but that is something that you can really use in the cases that you want to get in the middle of the code. So take it in mind, and when you need to solve some specific stuff, the descriptors may be useful for you. In this very presentation that is already published in the web, you can see the code, you can see it unless you have a telescope or something, but there is all the code for making everything work, so if you are interested, go paste that into a couple of dotpies and start to play some legal stuff because maybe Michael Jordan didn't say what I said, but, and that's all. Thank you. Liza here, and if you have any question, I'm very glad to not answer them. Oh, okay. I find this descriptor thing quite appealing because it's like an alternative to when you have to do metaprogramming to insert things and stuff like that, but I'm wondering how is it to debug? How hard is it compared to metaprogramming or doing everything more plain by them? It's not hard to debug at all because you have to think it like you are doing funny function calls. I mean, you have a code like this, you have a code like this, and the only thing that you need to take in consideration that in the case, for example, of doing Gimli.strength equal, whatever, you are doing a function call at that point. So when you read your code and you know that you're accessing a descriptor, you know that in that case, you're getting to shamping to that part of the code and that's all. The moment you understand that, you can easily follow the flow of the execution. So when you are doing the, well, I was more referring to the last part where you actually use a type to create a class insert in the methods. So I was more referring to the bug in that part. It just works. I didn't have to debug it really. It just works. Actually, when we created the presentation, it was three times more complex, everything. We found the way to make it simple like this that is not simple, but it does what it needs to. Can you go to the next slide when there is a target? I think in this one, it looks like it's looking at the name of the attribute, not the actual class. So if I call, if I'm the class character, I call something strength, but then I do equal to. There's nothing to do with the actual, what happens? This, yes, if you misspell this, it will fail. You can, you could check for the instance and the class name and it will be more robust. So you should check, in theory, on the right and not on the left to be more sure of what you're doing. Because it can, I don't know. You could. It will be better. This works. I mean, this whole board game thing, whatever, it's just an excuse for the talk. We really didn't do this for real, but this is just an excuse to use the script or somewhere. Okay, so another question. Is there a reason to use a Dunder dict and the score is for dict in the get and set before in the other slides and not set after, for example, because, is it because you can't use it there, maybe? Sorry, when? Where? In the Dunder get and Dunder set before. It's accessing done. In the Power Descriptor. Yeah. Here. Yeah, why not doing set after there? Does it work, maybe, or? I don't remember now, but I think there is a case where it doesn't work. I don't remember why. Ah, right. Right, if you access, if you do instance. Or, well, set after instance name and the value, it will be calling the descriptor itself. So you don't set the attribute, it's just mess with the dict. Okay, so how do you deal with inheritance when using class descriptors? Well, that's a very, this is interesting question because this is one case where you cannot use properties because, I mean, you are encapsulating code and you want to use inheritance, so you cannot use property anymore. You need to use proper descriptors. You just use it, it just works. There is no magic there. You can have parent classes with the others, methods or whatever, the descriptor inheritance, and it works. So basically you have to put class decorator in both parent and child class, but that case, Ah, you say not when created the decorator, but when you see it in this case? No, in class decorators for a character, for example. It does some magic with applying a power descriptor. I don't know. In that case, I don't know if I didn't try it. I mean, I remember this whole board game, power character, whatever, it's just an excuse for the presentation. We didn't use it in production or anything. So I would need to follow the code now. I didn't try it. There is another one here. In the previous slide, with the power decorator, is there any reason why you are setting the value into the instance dictionary and not into the power descriptor instance itself? Yes, because the power descriptor will use, will maybe apply to different instances. So it's a normal behavior, maybe not in this case, but you may have the descriptor instance to be applied to different attributes. So you normally start the stuff into the instance. It's what you normally see in the, everybody does. But what if another descriptor wants to instance, wants to access the same key in the instance dictionary? It doesn't happen in this case. If you have that case, you can start it whatever you want. Okay, thank you. I mean, there is no Python magic here. You just put stuff in some places and then use it. I mean, you could have a global dictionary with the key being the instance and the name and access that global dictionary instead of storing stuff in the instance. You can pick it, you can send it to the database. But it's pretty normal for the descriptors to mess with the instance dict. Yeah, great talk. Thank you. I have a question. Can you explain more why we are initializing the descriptors inside the class and not, for example, in the dunder in it? And can we change the descriptor during the runtime? Not on this slide, the previous one, for example. Yeah, here on the class, you initialize strength and magic inside the class and not in the dunder in it. I'm initializing it. The construction of the strength and magic. The what? The construction. There, the instantiation. Yeah, why? Because it needs to be a class descriptor. If it's not a class descriptor, it doesn't work like a descriptor. Sorry, sorry. If it's not a class attribute, it doesn't work like a descriptor. The magic of have a class having get or set and being a descriptor is only executed when that is a class attribute. So also see here that in the moment of instantiation, I'm doing self dot strength equal strength and also at this very same moment, I'm executing the descriptor's code. Thank you. I think that's the last four questions. Okay. So everyone have a very nice lunch. Thank you very much.