Understanding Python: Descriptors

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] welcome back to understanding python my name is Jake and today we'll be exploring python descriptors in this video we'll dive into what descriptors are how they work and how they allow for Unique attribute behavior in your code descriptors are a powerful tool that can greatly enhance the functionality of your classes and we'll be discussing a number of ways in which they can be used so if you're ready to learn more about this essential python feature let's get started you may not realize it but python actually uses descriptors all over the place for example in my initial video on basic classes we actually covered four uses of descriptors because not only are properties class methods and static methods descriptors but so are bound methods themselves their implementation is just some of the background magic that python does but exposing a simple interface for us so we don't have to worry about the details when we're writing our classes now descriptors themselves are relatively simple concept like many other subjects I've covered they basically just allow you to customize attribute access and behavior there are two main types of descriptors there are data descriptors as well as non-data descriptors it may seem a bit weird at first but we're going to start actually with non-data descriptors first and we'll explain things as we go along first let's just start by defining a simple class called yes this is going to be a non-data descriptor that just returns yes and the way that we do this is by implementing one of the descriptor methods that we have available to us and that's going to be thunder get now this takes a couple of arguments let's not worry about them just yet because we actually won't be using them for this particular example and we're simply just going to return yes and that's it we just wrote our very first descriptor there's not much to it we're still unclear of exactly what's going on here with instance and owner and what's really happening with get we'll explore more of that in our testing first we need to actually show how to use this descriptor so create a simple test class and we'll create a class level variable called Alpha because it'll be our first and this will just be an instance of our yes descriptor and from here we can start seeing what that looks like so let's load the file right now we have our two classes in here next we'll just simply create an instance of test class doesn't take any arguments and now we have tests dot Alpha which is always going to give us yes so that's all this descriptor does is any time that something tries to access the attribute here or access Alpha attribute is simply just Returns the value yes now realistically we could change this to anything we want you've been changing it to no but we don't want to do that now there's a lot more we could get into with non-data descriptors however since they're the most basic form of a descriptor I think it'd be more useful to build on top of that functionality when we get into Data descriptors themselves and we'll also explain what each of these arguments are that go into our descriptor methods so let's do that now one of my favorite ways to explore call patterns and see exactly what's happening when python interacts with magic methods like these is to just create noisy classes to figure out exactly what's being passed around so let's do that now with a noisy descriptor and it's simply going to be a data descriptor that prints out all of its actions and just like we started with our non-data descriptor we're going to start our noisy descriptor with a get method this time though instead of saying instance and owner I think I'm going to clarify what I'm really expecting to be passed in by saying object and object class starting to get a little bit of hints of what's Happening here so let's write a formatted print statement that'll give us a bit more clarity when we're trying to use this noisy descriptor a print statement just saying getting the value from object of type and then the objects class itself there's two modes that we're going to want to check here if an object is being passed in or if an instance is being passed in we're going to want to return ourselves getting a particular attribute from that object and we'll just say that the actual value is underscore noisy right because we need some type of reference on the actual parent and we'll default to none if it can't be found otherwise we're going to try to get it from the object's class same thing getting noisy otherwise we default to none and don't worry too much about trying to understand what's happening right here as we run this and start to look at what's being passed around a lot of this is going to make a lot more sense descriptors are one of those cases where you really need to see things running to get an idea of what's flowing where now to make this a data descriptor instead of a non-data descriptor we need to Define at least one of two other methods the first method is going to be under set of course take self I take the object that we're setting the value on and then the value itself and we'll simply just say setting value equals on object and just to Simply we're going to set it on object we're going to use underscore noisy again and pass in the value so when our descriptor is being told to set a value we're actually writing it to an underscore noisy variable on the object itself and then delete is going to be much of the same again let's just delete and we're just being asked to delete that from the object itself so we'll say that we're deleting noisy from the object I will do that with Dell adder or delete object and what we're going to delete from that is underscore noisy all right now we have our noisy descriptor defined give us a little bit of room and then see how we add that on to our test class so just like before we'll create a new class level variable this one will be called beta and it's also going to be a noisy descriptor now since we're not setting anything there by default let's go ahead and create a Dunder knit for our test class as well so it's going to take in self and it's also going to take in to be a bit more clear we'll just say value here and say self dot beta is equal to Value so without further Ado let's load the file and see what's going on okay so far nice and clean we've now tried to get set or delete any of the values here so we're getting right away since we haven't set the value beta here and the way that we're doing the lookup is we're just getting the attribute noisy from our test class if we were to try to get test class dot beta we'd say that we're getting value from object is equal to none this is important object is equal to none remember object is actually the instance of a class itself of type and we have object class is equal to our test class so yes the object class is actually being passed in there and since it's defaulted To None we get nothing printed out maybe for a bit of clarity we'll change this to nothing here so yeah with that clarity let's reload this and see what that looks like there we go testglass.beta same thing object is equal to none so it's not an instance which means that we're calling it from a class itself and it returns nothing here because nothing has actually been set now if we create an instance of test class we'll just pass in a value of 15. we see setting value is equal to 15 on object and here we have our test class object here that's the actual instance of test class itself so we look at the set we're passed in the instance and then the value now if we check test dot beta we have getting value from object is equal to that instance see same instance as above it can now get that value that we set on the test instance well where's that 15 coming from well let's look at test.dunderdict if we look in here we have what we consider in the python World a private attribute can't see me but I'm doing private in air quotes because we have the underscore that prevents it there so if we look here we should see noisy exactly so when we were calling set we were actually saying test dot underscore noisy is equal to 15. that's what's really happening here in set and when we do the get it's really just returning test dot underscore noisy or if that was set on the class it would be testclass dot underscore noisy and then if we were to delete test dot beta it says it is deleting noisy from our test class object so if we look at our dictionary again we now see that underscore noisy is no longer in our instances dictionary and if we try to call test dot beta it says nothing here so it's defaulting back to that behavior that we see when it can't find the value so you're starting to see a little bit more clarity into what's going on with descriptors and a little bit of the value that they give you well there is still more to this and one really important thing because if you stop here you're going to run into some significant bugs in your code and I'll show you exactly what I mean by that what happens if you want to use two noisy descriptors in this class said we want a gamma attribute here to also be a noisy descriptor let's see what happens we'll reload this create another instance of test and we see again set in the value 15 on that instance so far so good test.beta it's equal to 15. test dot gamma since it's not been set yet oh well what's going on here how come test.gamma is giving us 15. we never set that value there we never assigned a value to gamma let's see if we can correct that test.gamma and set this equal to something different literally something different okay cool test that gamma is now something different so if we look back at test.beta oh wait that's not something different what's going on here let's look back at our dictionary we still have noisy and now it's pointing to something different if we set test.beta is equal to five we could test out dick now noisy is five that's because both of these are using that same private attribute on our instance underscore noisy so in our current implementation we can't use more than one noisy descriptor but the authors of python are smart and they've thought about this problem and they have a solution for us and the form of that solution is through a new magic method called set name we have Dunder set name and this is going to take in three arguments we have self if owner and we have name since this is noisy we'll also make it noisy and we'll just say received name from owner now since we are a class we can store some information so what we're going to store is a private name you can call this whatever you want but for this example we're just going to keep it nice and simple and do an F string with an underscore and then name okay and then each time that we're going to use noisy in the rest of our class we'll just replace that with private name type a name and we can also replace that here this time we're going to say we're getting private name from object of type class so let's test this out and see what it gives us okay right away we see a couple new statements here looking back at our test class we see received name is equal to Beta from owner test class and then receive name gamma from owner test class so when it does this assignment here what python is effectively doing is taking our instance of noisy descriptor and calling set name on it passing in task class as the owner and then the name beta here as the name and what this allows us to do is understand which variable we're referring to what we're doing are get sets and deletes look at our previous example test is equal to test class passing 15 is the value here we see we're setting value equals 15 on that test class instance and if we look at our dictionary we see now underscore beta is equal to 15. you look up the value for gamma we see nothing there good we have isolation set a value for gamma sure we'll do something different in there and look up the dictionary again we now have underscore beta and underscore gamma actually storing the values that's much better so the big important thing here if you're going to write a data descriptor that you want to use multiple times within the same class you need to take that account some way and one of the best ways to do that is through set name all right so that's the basics of how you define a non-data or a data descriptor there's effectively the basic methods that are part of the descriptor protocol but what are some things you can do with these descriptors aside from just being able to print out what's going on with the descriptor what you can actually do with them is entirely up to your imagination here some common examples are implementations for plugins validation type checking caching lazy loading and a lot more but one thing I think is a fairly short and illustrative example is defining what could be considered as close to a true constant as you really get in this python space so let's create a new data descriptor called simply constant and I'll show you how I want this to look so we get an idea of what this spec should really look like and sometimes it's easier to write out what something should look like from a user standpoint before you start implementation so it can guide your implementation itself if you want a new variable Delta to be an instance of this constant but what's going to be the value of that constant well if we just stop here we wouldn't be able to change the value of delta so Delta would always just be equal to what in this instance well these are still just classes right and we can pass in values during class substantiation so we can set our constant here in our default for this will be a name Kiara understand that we want a value to be passed into the init let's Define art of course you have to take in self but we're also going to take in value and simply just store self.value is equal to value now the get is easy enough as well underget self making effectively ignore these but we'll just accept them anyway because we're just gonna be returning self.value and then for set since it's a constant will also be ignoring these and we're going to raise an attribute error and in that attribute error we're going to say we can't change a constant's value all right so we should have our constant but before we run it let's fix that typo there and that you're probably yelling at the screen about changing the any to and knit so it's an actual valid init method and we'll do this in we can ignore our noisiness from before create our instance of test class let's keep on passing in 15. and right now we should have Delta which is a constant test.delta is equal to Chiara now that's not stored anywhere in the instance dictionary because again remember it's a descriptor it doesn't need to store it in the dictionary and if we try to change Delta to something else make changed it raises that attribute here I'm telling us we can't change a constant value now if you don't want your constant descriptor to be that harsh you can absolutely have a chill out a bit because again this is our descriptor and we can Implement whatever Behavior we want in it so here we're going to say strict it'll be a new it'll be a new option that we can pass in the classes by default our constants will not be strict what does this strictness mean well we'll just check this here if self.strict we'll raise that attribute error otherwise if we're not in strict mode we'll just effectively ignore it we're not going to set anything on there we're not going to change the value but we're not going to erase that attribute error alternatively you could log something saying hey you know you tried to change a constant value but we'll just leave it here if we're not in strict mode then we don't need to raise anything keeping the naming going Epsilon will be a new constant we'll say I'm strict is the value of the constant set strict is equal to true now if we reload this create a new instance of test class again test Dot Delta it's equal to Chiara if we try to change it it doesn't do anything still gives us KR but it doesn't complain however we have Epsilon which tells us that it's strict and if we try to change it it will raise the attribute here okay cool now that we've made it through that and you made to this part of the video we'll wrap up with the easy way to do custom get set and delete methods on your class and that's by revisiting our property decorator from that basic classes video so we'll simply start with the property we'll Define our property method again keep that naming structure call it Zeta and this will return get adder himself it's going to look up just for ease sake underscores data none now something interesting that property allows you to do that I don't think a lot of people know this allows you to set custom Setter and deleter methods by reusing that property You just defined we have a new Zeta dot Setter decorator available and we can override what we just had self and value and we'll just say self.under Zeta equal to value and then the leader will be much the same Zeta dot deleteer again we're overriding Zeta this doesn't take any arguments and we can just delete the attribute himself which is underscore SATA all right so for one last time we're going to reload create an instance this time we have test dot Zeta right now it doesn't have anything in it change it to something now if we look at test.zeta we have something we look in the dictionary we have underscores data set there and finally delete test.zeta it deletes it from the dictionary so without having to implement our own custom descriptors we can use property to give us quite a bit of control that we otherwise would have had to have written a full-on class for and that wraps up this video now that you have a good understanding of how descriptives work try implementing them in your own projects and explore different use cases as always today's code will be added to the understanding GitHub repo so check the description for a link and of course if you have any questions or suggestions for topics you'd like me to cover let me know in the comments section keep up with the series please consider subscribing thanks for watching [Music]
Info
Channel: Jake Callahan
Views: 5,561
Rating: undefined out of 5
Keywords: python decorators, python, decorators, python decorator, decorators in python, python decorators explained, python decorators tutorial, decorator in python, python tutorial, python class decorators, decorators python tutorial, python for beginners, decorator, decorators python, decorators python 3, learn python, python decorators advanced, understanding python, python expert, understanding python descriptors
Id: 3QqURpf7Wjs
Channel Id: undefined
Length: 23min 41sec (1421 seconds)
Published: Sun Apr 30 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.