We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

How dunder methods rule Python under the hood

00:00

Formal Metadata

Title
How dunder methods rule Python under the hood
Title of Series
Number of Parts
141
Author
License
CC Attribution - NonCommercial - ShareAlike 4.0 International:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Identifiers
Publisher
Release Date
Language

Content Metadata

Subject Area
Genre
Abstract
[Python dunder methods] – like `__init__` – are sometimes referred to as “magic methods” but they are not! They are just regular methods! Functions that are associated with objects and that you can call with arguments. The only thing is... Python also calls those functions behind the scenes in certain situations! So, let us learn what that is all about.
114
131
AreaUsability2 (number)Point (geometry)String (computer science)Electronic mailing listElement (mathematics)Wrapper (data mining)Object (grammar)NumberRegular graphProper mapRule of inferenceSingle-precision floating-point formatCodeData miningDifferent (Kate Ryan album)Line (geometry)Software frameworkTouchscreenSlide ruleFunctional (mathematics)System callLetterpress printingRight angleBuildingRepository (publishing)WebsiteDot productComputer animation
Group actionTetraederUsabilityRandom matrixTouchscreenCASE <Informatik>Object (grammar)InterpolationRepresentation (politics)File formatSoftware developerString (computer science)Type theoryCuboidWrapper (data mining)Artificial lifeRevision controlSocial class2 (number)Electronic mailing listMultiplication signMessage passingDot productLine (geometry)Rule of inferencePoint (geometry)Computer programmingCodeDifferent (Kate Ryan album)Instance (computer science)Text editorRadical (chemistry)Right angleSlide ruleOrder (biology)Letterpress printingComputer fontComputer fileImplementationObject-oriented programmingStandard deviationSimilarity (geometry)Computer animation
Electronic mailing listGrass (card game)View (database)SummierbarkeitTemplate (C++)Formal languageSystem callSocial classGradientComa BerenicesGeometryPRINCE2Group actionSubject indexingContext awarenessElectronic mailing listObject (grammar)Poisson-KlammerPersonal identification number (Denmark)Key (cryptography)SequenceSquare numberData modelInstance (computer science)Pairwise comparisonNumberAttribute grammarSocial classData managementWrapper (data mining)Parameter (computer programming)Point (geometry)Multiplication sign1 (number)Order (biology)Standard deviationComputer fileRule of inferenceRight angleWeb pageRecursionMereologyRepresentation (politics)Equaliser (mathematics)Generic programmingDefault (computer science)Software testingType theoryRecurrence relationInteractive televisionSystem callLatent heatDifferent (Kate Ryan album)Slide ruleElement (mathematics)Letterpress printingCASE <Informatik>MultilaterationData dictionaryString (computer science)Information securityComputer animation
Computer animation
Transcript: English(auto-generated)
Let's see how Dunder methods rule Python under the hood. My name is Rodrigo. That's my face. This is also my face, so you can find me online. What I do is I work for Textualize on the textual framework and I also like writing
about Python over at that website. Teaching really is a passion of mine and that's why I came here last minute and I'm taking the risk of, I don't know, making some mistake, but I'll be trying to share with you some of the things that I like about Python.
So all the slides and the code that we will be going through are available at this repository, so don't worry about taking pictures. You can take pictures, you don't have to, and yeah, let's get started. So the goal of this short talk is to answer the question, what are Dunder methods? And the main key takeaway I want you to take from this is that they are not dark magic.
For some reason, some people call them magic methods, but they're not magic. Actually, they make a lot of sense. They're regular methods. They just do some interesting things and I want you to understand what these interesting things are.
So the proper name, if it's not magic methods, it's Dunder methods. And why is it Dunder? So what the hell does that mean? Dunder just stands for double underscore. So the Dunder methods are those methods that have the interesting looking names like Dunder-init or Dunder-stir, and there's plenty of them.
There's too many to count. So there's a lot of them, and I'm not going to enumerate all of them because that's not the point. I just want you to familiarize yourself. These methods that start with two underscores and end with two underscores, they just have the name like that because Python uses them for some special purposes, and we wouldn't
want the user to create an init method with i-n-i-t, and then we wouldn't want that to override whatever special method Python is waiting for, and that's why they have the double underscores, to make it very, very unlikely that the unsuspecting user
might override those methods by mistake. So I want you to, I want us to start by taking a look at these two Dunder methods. So we're going to explore them a little bit so that you can see, so that you can see them in action, and that you really understand the key point of this talk, which
is they're not magic, they're regular methods, they do regular things, okay? So if I manage to spell Python, everyone comfortable with this? What happens if I press enter?
Can no one tell what? Okay, so we're going to get a three, okay, fine. Is everyone comfortable with this? What do I get if I press enter? So everyone is comfortable with the line I just typed? You're sure? Because if you are, then you have, quote unquote, you have to be comfortable with
this. Who's uncomfortable with this, at least for now? Who's uncomfortable with this? Okay, I used to be uncomfortable with this too. But it's fine because three dot underscore underscore str underscore underscore open and close parentheses just gives me a string that looks like the number.
Does this look reasonable to you? Because the objective of that Dunder method, what it does is it takes whatever object you have and turns it into a string so that it can be printed, for example. And so when I called print, what happened under the hood was Python took the three
and it looked for a str method. And if it finds a str method, it calls it because a method is just a function, right, so you can call the method. It gets that thing back and then it shows it on the screen. So that's what Dunder str does.
It's just that. Does this make sense to you or who feels this is weird? Wait, on one go, everyone may... Okay, so then I think we're done for now and we can all go to lunch, right? So that's the whole point of these Dunder methods. Dunder methods are regular methods.
You can implement them yourself. And the point is there are special occasions that are well-defined, okay? You can read what those occasions are. There are special occasions in which Python calls those methods automatically for you. Because in...
Maybe it's easier if I look at here. Yeah, so in this slide, I did not call str automatically but Python did, all right? So that's the thing that you have to bear in mind. There are going to be situations in which Python will call those Dunder methods for you. And that's why people call them magic because you don't call them explicitly, Python does. But there are well-defined rules, so they're not magic.
It's just the way it works. Let's take a look at another example. I have a list here, right? So what do I get if I do my list Dunder stir? Who can guess what's going to show up on the screen?
Exactly, a string literal of the list. That's just it, which is going to match what I get if I print my list, right? That's the only purpose of Dunder stir. It looks funky. What, what? No, no, no, it does, it gives you string first and if it doesn't find Dunder stir, it
will look for Dunder wrapper. What it does is it calls wrapper on the elements themselves. So two, three, and four. So that's a very nice distinction. But thankfully I wasn't, I didn't make a mistake yet.
I'll make some mistake. It wasn't this one yet. Okay, but thank you, thank you. So yeah, that's what's happening here. And actually to bring your point across clearly, we were actually going to talk about that and there's one thing we can do to make the distinction here. So if I do, I need to think for a second.
So if we do, let's create a string, not this one. So my string is going to be my name because I don't have any imagination. Now if I do, let me think, if I do string of my string and if I do wrapper of my
string, can you see that they look different? Can I, yes, I can move up, I'm sorry for that. Can you see that they look different? One is just the string with seven characters, which composes my name. And the other is a string with nine characters.
It's a string with a string inside. So they are different. Do we agree that they look different? Awesome, because they do, because if you don't agree with me, then there's nothing I can do for you. And these two built-ins are calling two different Dunder methods.
All right, who can guess what Dunder method is getting called by this function? We've seen it before. Dunder stir, all right, so then I know it's dangerous to generalize over one single example, but let's do it anyways. Who can guess what Dunder method is getting called when this built-in is called?
Dunder wrapper, exactly. And so these two methods, they might seem like they do essentially the same thing, and they do similar things. They are supposed to build strings out of your object. But stir is supposed to be for pretty printing, let's call it that. It's the nicest representation of your object.
And wrapper, you can think of it as the debugging, quote, unquote, the debugging version of your representation. So when we do the next exercise, let's call it that, you will see what the main purpose of string and wrapper, like the distinction, you will see what the distinction is.
And this also comes in inside of lists. So we will see that they are different, even though they look similar. So actually, no, I don't know why I'm checking my slides again. So let's actually go to an editor, and let's open a file. Let's call it person.py.
And I want to, is this big enough for the people at the back? I can increase the font size. It's, okay, very nice. So who's comfortable with me typing this? Sorry, I know I asked the question, but I wasn't looking at you because I cannot, like typing and talking at the same time is already hard enough.
Typing, talking, and looking away, that would be insane. So who's comfortable with this, which isn't helpful. Who's not comfortable with this? Okay, fair enough. So why are you, okay, so if maybe you're not too comfortable with this, are you comfortable, or do you become more comfortable if then I add this line of code?
And John Smith has already made an appearance in one of my tutorials later this, earlier this week. So this should be a familiar person. Now I'm having issues with sizes. Now if I run this,
can you tell the people that raise their hands, can you see what's happening here, or are you still uncomfortable? Okay, I see someone squinting. Is it too small at the bottom? Okay. So essentially what I'm doing here is I'm defining a class person, right, so that I can create objects that are different people.
For example, in here I want to create some Python thing, some Python object that represents John Smith. And what I use and what everyone uses when they're creating classes is this funny-looking method, underscore, underscore, init, underscore, underscore, that looks very magical
if you're just starting out programming and you're just starting out object-oriented programming. But this method is no weirder than something like this, than Grit, and then you just, I don't know, you print self.first, dot, no, this is the weird order, so hello someone, right? It's a regular method.
Like these two methods, they're equally complicated. If you feel comfortable calling this one and implementing this method, there's, other than the weird looks, there's no reason to feel uncomfortable about this one
because this method is something you can call. The deal is Python has a very specific rule that determines, sorry, that determines that the init method is going to be called for you. And what rule is that? That rule is, can I, sorry, wrong combo, when you type something like this, when you create an instance of a person,
Python will create a new object. It will create an empty box. And then Python will give the empty box to some special method so that the empty box can be customized and become a person. And the method that's responsible for making that customization is the init method for initialization.
And so the init method takes an empty box and in here, this empty box gets a first and a last name. So that's what dunder init does. Why does it look weird? Well, because I didn't call it. It wasn't called here. I didn't explicitly write dunder init.
But it's being called and we can see that and this is a tip that I give you when you're playing around with dunder methods. Just add prints to make sure that they're being called when you think they are. So if I rerun this, will I get any messages in the terminal? Now this is maybe too low.
So let me do the following. Let me go back to this, over here, close this one and maybe use this one in here. So what will I get on the screen? Will I get anything on the screen just by running my piece of code? Who thinks the screen will be empty?
So everyone sees that I'm going inside init. Again, Python called it for me. That's the whole point of dunder methods. All right, am I repeating myself too much? Am I making my points across? I think that's how you use English, right? English is hard.
Python is easy, English is hard. Okay, very nice. So then let's see why would I want to use dunder-ster and dunder-repper in this class? So right now, what will I get if I run this? If I print John, what do I get?
I get an object, I get trash. I get this thing right here. Yes, I get repper of this person. So what's happening here is I did not tell Python how to turn this class into a nice looking thing. And so Python falls back to some other thing.
Let's leave it for another talk or the reason why it gets there. And it composes this because how weird would it be if you created classes, you tried to print them and you just broke Python? It would be weird, right? So Python makes sure that you can always print whatever you just created. But this is an ugly fallback.
I don't even know if this is John or Anne or some other person. And so what you can do is you can implement dunder-ster or dunder-repper. Now again, there's a distinction. If you can only implement one, because you only have 10 seconds or you're live coding in front of an audience, you will implement dunder-repper because it's the fallback.
It's also the one that you want to implement for the developer. So for the debugging purposes, this should be enough that you know exactly what object was being printed. And so in this case, what I would do is if I need to know exactly what's being printed, I'll make sure that I specify in some way the class that this belongs to
and whatever attributes I would need to reconstruct it. And so something standard is to, standard at least for me, is to write something that you could copy and paste to recreate the object. Everyone comfortable with string formatting here with the interpolation?
So if I run this now, who can tell me what I'm going to get? I'm going to get exactly what's written here in line 11. I'm getting the, actually I missed the quotes if I wanted to be able to copy and paste, but I get this. So Python was calling wrapper for me, but earlier I said that when you try to print,
Python will call dunder-stir, right? Well, the thing is, I don't have dunder-stir here, and so Python fell back on dunder-repper, but if I do implement dunder-stir, sorry, it's confusing, if I do implement this and I say something just like, I don't know, self.first, and if you believe me, then you should know,
or you should be able to say that what's going to get printed now is just John, and indeed it is. However, and hence why this might be weird, is that if I have a list with John inside, now because I'm inside a list, Python thinks it's best to just use the debugging,
quote, unquote, representation, and so if I print my list, then I'm back to wrapper. So you don't need to memorize these rules right now, that's not the point, it's just so that you can see that different dunder methods, even if they look similar, they will always have different purposes,
and they allow you to customize the way that Python interacts, sorry, yes, the way Python interacts with your objects. So all of the Python syntax, there's nothing magical going on, it's all synthetic sugar for specific dunder-method calls, and so the more dunder-methods you know, the more you can customize the interactions of your objects with the syntax of Python.
So this is just a quick summary of what we saw, the dunder-method init is called when the instance is created, and the dunder-method str is called when the object is converted to string. I hope this is fine enough for all of you.
Now some common and useful dunders that I implement almost all of the time include dunder-init, dunder-str, and dunder-wrapper, and dunder-eq for testing equality of your objects, because by default, the only two objects, the only two instances of your own classes that will compare as equal are the same instance.
So if I create two John Smiths, they will compare as different, and I would need to say, well, if the person has the same first name, the same last name, and maybe the same social security number, then it must be the same person, right? And right now, the thing that happens is only if it's literally the same object,
it's going to compare as equal. It's the same thing as saying if you have a $1 bill and a $1 bill, and if you compare them, Python is going to say it's false, because the serial numbers are different. But you can go to a supermarket and either bill is fine, right? They're the same value.
And so you would want to use eq to say if the value is the same, then they're the same bill for comparison purposes. So now, let's take a look at another example, and that's how sequences interact with other methods. Sequences rely on a other method that's called getItem,
which is used for getting items, and a method called setItem, which is used for setting items. So I think you're all above the requirements for this talk and some others, but these two are like the main ones if you want to think of accessing items and setting items.
And so for example, an exercise for you, not for right now because I'm almost done, but for you to take away home is what if you wanted to implement a none list that always returns none? Whatever index you access, it always returns none. Then you'll want to implement getItem
and make sure you return none. And if you want to, sorry, I'm getting ahead of myself. So that's when you access items, you call getItem, and if you want to do assignment, then you will use setItem. That's the point. And let's see this in action. So this is the challenge.
Actually, no, so I'll leave, I have another one which is nicer, but also slightly harder, so I'll do the simpler one with you, and then you'll have the slightly harder one as a challenge for you for later. So let's implement this real quick. How does this go? Let's call this the noneList.py.
So you're going to create a class, right? And to initialize, let me just do, I don't care about initial arguments because in here I don't have any. I just want to save a place. I just want to have some place where I can save the values that people already gave me. So let's do something like self.values
equals an empty dictionary. Now, the main behavior here is when I get an item, what does Python need to tell me in order for me to produce the correct item? I need the index. So this is the argument that Python will give me automatically. So when I type something like this,
my list, square brackets, zero, square brackets, then the zero will be the index argument here. And so what I need to do is I just check if the, actually I can do this. Return self.values.getIdx. So if values is a dictionary, .get,
we'll try to fetch the value associated with the key. And if there's none, it's going to return none. And so running this should already give me part of the behavior. What was the name of the file, noneList? Did I do something wrong? No, I heard someone say something. So noneList, zero, is it going to break? Nope, it gives me none,
but I didn't print it so we can see it. Everyone comfortable with this? Because I want to be able to return values that get set later. The spec wasn't clear. My point is after I assign something,
I want to be able to get that value back. And so when I set an item, what do I need? I need the key. It was being set to end the value itself. So let's call it actually key value. So key here, key value, and then what do I do? Well, self.values, key equals value.
And now if I rerun this, I get a noneList. I set some value on it, and I should be able to get it back, hopefully. Okay, so it seems like it's working. And it should still print something for an unknown key.
It should print none. So if you ever want to create your own sequence, then you'll definitely need these two. Or if you want a read-only sequence, then you can leave this out. Or if you want a write-only sequence, then you would leave getItem out.
And that's the whole point. These are methods. You implement them, and Python does things with them. Now the challenge for you is then to write, so the recursive Fibonacci, we all know that, so let's do something nicer. You're going to create a generic recurrence relation. You use setItem to set the base cases,
and then your getItem should use the base cases to figure out whatever items you ask for. So that's the challenge for you. Come find me if you want me to be clearer about the spec or if you have a possible solution. And there's dunders for everything, like I said. If you want to do membership testing with your objects,
there's a dunder for that. There's a dunder, two of them for context managers. There's dunders to customize attribute access. There's dunders for everything. So if it's in the Python syntax, there's dunder methods for that. So the two main references for this talk are an article I wrote about it
and obviously the Python documentation. It has a very comprehensive page about the data model which respects all of these dunder methods. There's also a bunch of chapters on a free book that I wrote about this, these dunder methods, string and wrapper, bool, other stuff.
And everyone always asks what I made my slides with, so it's this thing, snipify.com. Yes, it's awesome. No, I have no affiliation with them. And I don't think we have much time for questions. I'll be around. Feel free to join me in my poster session right now and for the sprints in the weekend.
Thank you.