 Thank you. And thank you for being here. Yeah, so today I will talk about Asperb, Abastard-based classes and why I think they are useful and a useful thing to know in Python. First of all, that's me. Too much text, so you have a recap at the end. I want just to point out that I also work sometimes and I am DevOps at the moment. I'm just doing a lot of AWS and GNEX more than Python. So today's talk is divided into three levels or sections. So during level one, I will talk about polymorphism, behaviors and delegation and collections. So some concepts that I think we should know as Python programmers, as programmers in general, and collections is a module that is worth knowing in Python. In level two, I will talk about registering Asperb-based classes, finally, and categories. And in level three, I will go a bit deeper into the topic and talk about how you build your own Asperb-based classes, meta classes, and how to use them as interfaces. So Python is a language that pushes the object-oriented programming methodology a lot. So one of the tenets of object-oriented programming is that behavior of a structure. So you, we as Python programmers, we should consider behavior of classes more than the structure. What do I mean? that At the end of the day, I'm not interested in knowing if something is a list or other things, but I'm interested in knowing if it behaves like a list. So I'm interested in knowing if your class or your instance can be sliced, if I can append elements. I don't, I don't, I'm not interested in knowing if you inherit from list or if you implement it from scratch. And this is a reason why in Python we push the EAFP paradigm a lot. So it's easier to ask for forgiveness than permission, right? So we try and accept, we try to use something and then we accept because we are not checking if some OBJ in this slide is subscriptable. It's, you know, if it's a list, if it's something that can be sliced, no, we just try it. And if it doesn't work, we do something. And this is, and we do this because we're interested in the behavior. This is an example of behavior. I have two classes, list view and item view, sort of like Django views. They inherit from our parent class, which is view. And the parent class doesn't define, sorry, I didn't have enough space in the slide, but the parent class doesn't define the status function, the status method. And I implemented directly in the list view and the item view. So there is no way for me to know, to, to say that list view and item view implement the function. I mean, I cannot say they inherit from view. This is not the problem because view doesn't define the status class, the status method. So this is an example of behavior for me. Those two classes behavior in some way. They provide a method. So the question is how we, how do we check how an object behaves? And one thing that we can do is this one. So we can try and call the method, for example. Please note that I'm not calling status. I don't have the parentheses. I'm just asking for the method. Okay. So I'm doing it because I hope Python, well, I hope, I know that Python raises the attribute error if the sumOBJ doesn't contain the status method, right? But I'm not actually using it. So this is, well, this works. I don't really like it because it's like three lines of code to just perform one check and doesn't, actually doesn't do anything. More than that, if I have to check a complex behavior, like for example, my object behaves like a list because they have a complex algorithm in which I have to slice, I have to append things and whatever. I have to check three methods and possibly more than that. And this is well, ugly, unmaintainable, and I'm wrong because I am checking the structure and well, in Python there are ways to provide methods without actually putting them in the classes. So I would say this is in general not a good solution. I don't like it as a programmer. The temptation we have and probably already used it a lot is to use instance. So I can say if sumOBJ is an instance of list, in this case, I actually check that myOBJ behaves like a list. It is a list. This is wrong because it is checking the type of the object. What if I implement a new class that behaves like a list, but it's not a list, this fails. This doesn't pass, but the code works. More than that, if I start saying, well, I want an object which is a list or a tuple or a string because, you know, I'm not just checking for a mutable sequence in this case. I mean checking for something that can be sliced and the three things, list and string and tuple, can be sliced. And then he comes and says, oh, I invented a new tuple or whatever, and I have to add it, so it becomes a maintainable very quickly. The perfect solution, in my opinion, would be to have something like behaves like. It's a function that says, yes, yourOBJ behaves like listBehavior. Well, listBehavior is a class, in my dream, that describes a behavior. Okay, that says, yeah, performs this operation, does this and that. Yeah? Okay, good news. We have something like that. First of all, Python is based on delegation. These are very important things to know about Python, that a lot of things in Python are implemented by delegating things. For example, you are familiar, I think, with magic methods of Python, like dunder, add, dunder, itter, dunder, contains, whatever. This is a delegation, because I'm not providing some code in Python core that says that object can be sliced or contains something or can be added. It's the object itself, the class, in this case, that provides a method that says, yeah, it can be added to whatever or it can be sliced. And this is important, because actually it's the mechanism that core developers leveraged to implement a solution to my problem, to my behavior-checking function. This is the original version of, is instance, some Python versions ago, well, a very simplified version, obviously, but the concept is that what I'm doing is to check. I'm passing a class and then checking that the class is in the basis of my object. Basis is an internal attribute that lists all the parents. Actually, the original version is a bit more complex, because we have to somehow climb to the inheritance tree. But this is the concept. I'm checking, I'm saying, yeah, Python does this, is instance, checks for the presence of the class. It was changed to a new version, which is based on delegation, again, a simplified version. Is instance now says, that object is an instance of a class. If that object says, yes, I am an instance of that class. So the object, sorry, the object, the class says, yeah, a method which is called instance check that accepts the object and checks for something. And that checks for something is extendable. I can create new algorithms. Okay. So this is not provided by Python in a core code. So now, this is possible. It is possible to say, if is instance some of the J list behavior, because is instance uses the internal instance check method of list behavior. We just need to create some class, like list behavior, that can check for the behavior, right? Enter collections. And collections is a module that you can find in the standard library. Yeah, it's a module of classes that, as I wrote, that represent interesting behaviors, like mutable sequence, sequence mapping, whatever. Okay, go and check the documentation. This is an example. I import collection and I say, is instance a string of a sequence. And a string is a sequence. It can be sliced, for example. So the check is true. But a string is not a mapping. It's not a dictionary, right? There's no key value pairs inside. So the check is false. And the link there is the documentation of the collections module. I advise is to go check it and start using it, because it's a very powerful module. It doesn't happen that often in Python that you have to check, really check of different, very different behaviors. But sometimes I had my code, the need of checking for example, if something was a string or a list. It happens sometimes with APIs. I am accepting a simple object, a string, a text, or a list of texts. And you cannot tell them apart by just trying to slice them. Because if you create a for loop on your parameter, the for loop on the string works, because the string is sliceable. It can be sliced. Okay, so to level two. Yeah. What is the meaning of being a subclass in Python? If you know some Python, you could say, well, being a subclass is something that you can do when you inherit from another class. And actually, this is true. It's one of the two methods. This is what we call real subclass. You inherit from a class. So child class inherits from parent class. And the relationship between the two classes is very important. It's on the bottom. So child class knows parent class. If you have an instance of child class, you can go and check around time that parent class is the parent. It's in the basis attribute. But parent class doesn't know anything about child class. It's a responsible parent, okay? Just spawns children and doesn't know anything about them. So you cannot go to parent class and ask, can you tell me all your children in my code base? No, it doesn't work. The second method is to be a virtual subclass. This could be new to you. And actually, it's called registering in the object-oriented world in Python as well. What you do is to register, or register, probably, the child class into the parent class. And in this case, the relationship is the opposite. Parent class knows that child class is a child class, sorry, open-intended. But child class doesn't know anything about parent. You are just assigning child class to a parent. And this is important because classes that can register other classes are called abstract-based classes. So this is the definition for today's talk. An abstract-based class is something that can register another class. And in the collections module code base, you can go and check it on GitHub. I found this code, these lines, which are very interesting because it says sequence, which is a class, an abstract-based class defined in that module says, I register tuple, I register string, and I register range, for example. So those three things are sequences because sequence registers them. And a list is also registered as a mutable sequence, which is not a string, is not a mutable sequence in Python. One very important thing to remember is that registering is a promise. So Python does no absolutely no check. So if you register, sorry, I go back. If you say sequence registered tuple, so there is no check done by Python about tuple. So tuple provides some methods, tuple has some attributes. No, it's really just a promise. An example of this. Here I import collections. I define a code, here I import collections. I define a class, my class, which is empty and doesn't inherit from anything but object. So it's pretty empty. If I issue is subclass, my class of collection sequence, obviously I get a false. Luckily this is not a list, but here the trick, I register it. So I say collection sequence, register my class. I say my class behaves like a sequence, and Python dutifully says it's true. Okay? So it's a very powerful mechanism, but with great power comes great responsibility, right? Absorbs-based classes are categories, just labels, okay, tags if you want. It's a way to say yes, this class behaves like that, but it's just, you know, it's a promise. I'm tagging my class as a sequence or a mutable dictionary or mapping or whatever. You are promising yourself or the other programmers that your class is behaving like you want, like you promised on level three. How do you build your own Absorbs-based classes? And this is straight from the docs because the example is so good that it's not worth implementing it. So from ABC, which is the module that defines Absorbs-based classes, I import ABC meta, which is a meta class, more on that in a while. I define a class, which is just a plain empty class, my ABC, and I define the meta class of that class is ABC meta, it's just code, okay? My class can be empty. And then I can use, magically, sort of, use register. So I can say, for example, my ABC register tuple. And what happens is that I can say that tuple is a subclass of my ABC because I registered it. And I can say that an instance of tuple, like the two parentheses there, are an instance of my ABC. So it's pretty easy to define an ABC. Why meta classes? First of all, don't panic, okay? There's a reason why I wear this t-shirt today. Meta classes are used by Python programming to scare children in the night if they don't go to bed, right? So I want to show you that meta classes are not complex, not more difficult than classes. Here, I have a very simple Python code, I think. It's a class definition, an instantiation of that class, and I use it at the instance. So book is a class that defines just an attribute, answer, 42. When I instantiate the book, I can use the instance and say, please, instance, give me the answer, and the answer is 42. It's pretty easy, I think, everyone here understands this code. This is the user of a meta class. I define the meta class, which is a class, it narrates from type, has an init with some parameters, and I can define something inside. So for example, self-answer, 42, sounds familiar. I can link a class and a meta class, so I can say my class book has a meta class, and I can use the class and say now, please, class, give me the answer, and the answer is again, 42. I want to compare the two. So on the left, you have the class instantiation, on the right, you have the meta class definition, and I think they are pretty much the same thing. So you have a definition of a class or a meta class. You have a use of the class or the meta class, and you have an instance of that thing. Why? Because meta classes, so classes in part are ideas, right? So it's something that is sort of a template of a concrete instance. And a meta class is the same thing. It's the template of a class. I want a class to behave in some way. The class, not the instance. I create a meta class. I think it's pretty easy. If you know how to create an instance from a class, you know how to create a class from a meta class. And why, yeah, it is simple in my opinion. Why are we using meta classes? Well, for instance, because we need the classes to contain this register method. We started from the need of having a register method in the class. We want the class to be able to register other classes because we want to tag them, right? Like a stamp. And meta classes can put things into the class. So we use the meta class to say, now I get my ABC class and I put inside the register method. And magically, I have it because of the meta class. Once Alex Martelli, who is here, asked me why don't we use decorators, for example, can be done. I decorate my ABC class and I put the register method inside. Well, decorators can't be inherited. So if you inherit from a class that is decorated, you don't inherit the decorator, but you inherit the meta class. So meta classes are more powerful, in this case, than decorators. Oh, yeah, sorry. I have a slide on that. You inherit the meta class. Here, you see, I define my ABC, which is an ABC meta, and then other ABC, which inherits from my ABC. And I can use other ABC just like I used my ABC. So, inheritance works here. It's important. Last two slides about different use of abstract-based classes. I'm not very fond of it, but it's worth mentioning it because it's in the documentation. You can use abstract-based classes to build interfaces. If you are familiar with other object-oriented programming languages, like Java, for example, you know that there's a concept called interface, which is something that enforces a structure of the object. So, for example, you say, my class has to contain the status method. And in Java, if you do this, the compiler complains if you don't include the method. So, it doesn't compile. In Python, well, for many years we didn't have that, and now we can do it with the abstract method decorator, which is defined in the ABC module. So, you just define your class like we did before. It's an ABC meta. Okay. And you can put an abstract method, which is something that gets checked when you instantiate the class. So, in the red box, you see what happens if you try to instantiate the status interface. It says type error, because you cannot instantiate an abstract class. Why is an abstract class? Because it doesn't contain an implementation of the status method. It's flagged as an abstract method. If you inherit from status interface, in this case, and you don't define the status method, Python complains with a type error saying, you cannot instantiate it. You have to do it. I'm not particularly fond of it, but it's my personal opinion, because I think Python is a very, very dynamic language. So, you strongly believe in providing behaviors, flagging things, and not enforcing the presence of methods. But it can be useful. So, if you want to use it, go and check the documentation, because there are other decorators, abstract property, for example, attribute, and so on. And that's all. Questions. Was it too fast? Sorry. Hi. Thanks for the talk. In the beginning, you briefly mentioned that it's not possible to... Now, please. Can you hear me? Okay, cool. In the beginning, you mentioned that it's not as easy to distinguish between string-like things and list-like things. You mentioned an API example that takes either a string or a list of strings. You said it's not possible, and then you went a bit deeper. Can you give advice on what is a proper approach to this without falling back to the ugly type checks, as you said? Yeah. Well, actually, it's an example of... Well, I had to implement it, and you can do it... Yeah, for example, if you use a collection sequence, that doesn't work, right, because the string and the list are both sequences. You can use a mutable sequence, because list is mutable and string is not. Obviously, if you need to tell apart two things that are mutable and sequences, you cannot use collections, right? So, if you define something which is like a list, but is different, and you want to tell a list and your class apart, you have to define your own abstract-based class and say, okay, I label my class as... I don't know what... my category, and then I check that. From the collections, you can do it with mutable sequence. So, yeah, mutable sequence is pretty easy to do. Also, thanks for your talk. I wanted to ask, when you create a child class, then the parent class gets put in the MRO. And I was wondering when you register, where is this information internally held in the class, if you know? I don't know. Actually, I never investigated it. Was my question not clear or you didn't... So, if I understood the question, your question is when I register here, where is the concept, the information about this... Well, I don't know exactly where, but it's inside my ABC class. So, in Python, classes are objects. So, you can do, for example, my ABC dot down the name, and you get the name of the class. And inside the... Actually, I don't have an answer, so I don't know the name of the attribute. When I was investigating Python object, I found deer as a very useful function. So, try and go and check it. I tried, I didn't find it. That's why I'm asking. Okay, it's an interesting... I put deer on the sequence, and I was trying to find, like, when you register, but I didn't... We can do it later and try to figure out. Very interesting. Thank you. Okay. Anyone else? If not, I've got one question. Okay, so, back to the question of why... why a meta class and not a decorator, have you thought about why just not make... register a class method and then inherit instead of having a meta class, maybe? Well, I think it's because you want to... Okay. In Python, you can have multiple inheritance, right? It's a hell. So, it's fine for very simple cases, but when you try to have too many parents, the problem is that you don't... you know, theoretically, the methods of resolution order, right? But in practice, it sometimes is very complex. If you use Django, sometimes, we'd make sense, you really don't know who defined that method. So, the problem with inheritance is that you create very big inheritance trees. So, I think the point is to avoid pushing further inheritance, which is already a bit too... abused sometimes with forget composition, you know? So, it could be possible, yeah, in theory. Well, in practice as well, but it practically leads to the use of multiple inheritance, and I like they don't take it this way, yeah. Okay, thank you. Any other questions? If not, let's have a big hand for our speaker.