More Special Methods
00:00
In the previous lesson, I showed you even more about descriptors than was covered in part one of the course and gave you an overview of .__slots__
. In this lesson, I’ll continue down the dunder rabbit hole, and if you’re lucky, you’ll see a smiling cat. Is an Alice in Wonderland reference too obscure?
00:18
Continuing on our journey to dunder all the things, let me remind you that everything in Python is an object. So when you go to do key things like operations, they are actually implemented with methods. They’d have to be ‘cause, well, they’re objects. And using dunder methods, you can perform operations like adding, subtracting, in fact all the math things, as well as bitwise operations, convert things … You’ve seen .__str__()
and .__repr__()
in previous parts of this course, and this goes for all conversion.
00:52
Calling bool()
or int()
or float()
invokes a corresponding dunder method. That also goes for comparison. All the things you might do in an if
statement, like comparing for equals, are done with dunder underneath.
01:05 The cool thing about all this is you can override these methods in your own objects and be able to use these language features on the objects themselves.
01:14 Let’s go visit the Great Down Dunder. No? Enter the Dunder Dome. Dunder the Boardwalk? Okay, I’ll stop now. Nah, one more. Dunderworld, a movie about tight pants and object methods. Entertain me.
01:29 Leave one of your own in the comments.
01:33 A common reason to override operations is to implement classes where those operations are natural. If you want to build math concepts like matrices or vectors, there’s no reason why you shouldn’t be able to take advantage of the math operators, adding two of them together, for example. Of course, most of this is already in the standard library or third-party libraries like NumPy, so don’t write it yourself, but if you wanted to, this is what it’d look like.
01:58
A Vector
class takes an .x
and .y
component, and .__add__()
is what you came here to see. Overriding this method makes the Vector
class work with the add operator.
02:08
The signature takes on one other object, the thing being added to this instance. The result of .__add__()
should be a new vector with its attributes being the addition of this vector and the one passed in. To make this visible in the REPL, I’ve also implemented .__repr__()
.
02:27
Of course, this class is incomplete. A good Vector
class would implement all the math stuff, but this is enough so that you get the idea. Importing …
02:41
creating a vector … looking at the vector, which called .__repr__()
…
02:50
creating another vector … and adding them together results in a third. .__add__()
for the win. It’s a Dunderful Life. I told you, leave a comment.
03:00 I’m not going to stop anytime soon.
03:04
There are a ton of these dunders in the base object, which you can override. Using dunder methods, you can create context managers, muck around with dynamic class creation and instantiation control, check whether something is inside something else, is an instance of something else, or how long it is, control what happens when the format()
function is called.
03:27 In a previous lesson, you saw how to control some of those attribute management things, but it goes much further. There are multiple mechanisms for getting, setting, adding, and removing attributes, and that’s not it.
03:40 There are over 125 dunder methods and attributes on the base class, which you automatically inherit just by writing a class definition. There are so many that if you laid them end to end, you could use a submarine to travel 20,000 leagues—say it with me—dunder the sea. Before moving on to the next lesson, a quick little tangent about things that can go wrong.
04:03
Depending on what you’re doing with a class, you can see different kinds of errors. An AttributeError
is raised if you attempt to access an attribute that’s not on the object or class. A TypeError
is raised if you’re trying to do an operation on the object that isn’t implemented. For example, calling len()
, which invokes .__len__()
on an integer. And some classes that expect to be extended will define a method and have it raise a NotImplementedError
.
04:30 That way, if the extender didn’t override it, you’d get this exception. There’s another way of accomplishing this, though, called an abstract base class, which I’ll cover in a later lesson. If you’re writing classes, there are a few common mistakes to look out for.
04:46
Forgetting to include the self
argument in a method will cause either an exception if you had no arguments at all, or having a reference to the object stuffed in whatever you wrote as the first argument.
04:58 I tend to make this mistake when I decide a function that I’ve written really shouldn’t be a function and turn it into a method, moving it inside my class.
05:06 Another case is confusing when you are supposed to be using the class itself vs an instance. This isn’t as common as the previous error, but can happen when you’re supposed to be passing around a class reference rather than instance reference to, say, a utility method or function.
05:23 Somewhat related is confusing what are class attributes and instance attributes.
05:29 I covered a tricky version of this bug in part one of the course, where I showed the accidental creation of an instance attribute instead of modifying an existing class attribute. Not quite mistakes, but some things that can lead to problematic code are using non-public members outside a class.
05:48 Things with leading underscores are a sign by the coder that they might change or have side effects that you’re unaware of. Generally, if you aren’t inside the class, you shouldn’t be using non-public members.
06:01 The entire third part of this course could be summed up in don’t misuse object-oriented relationships. Coders new to OO or coming from object-oriented heavy languages will tend to create oodles of objects.
06:13 This is often overkill in Python, and likewise, you can always tell a programmer who’s recently discovered the beauty of operator overloading when they tend to overload the operators to save typing.
06:25
Instead of writing a named method that is clear, they might override .__add__()
instead.
06:30
My general advice here is stick with the intent of the operator. If you’re not actually adding things together, you shouldn’t override .__add__()
.
06:39 Classes aren’t just for you. They’re for the core programmers as well. Next up, I’ll show you some classes in the standard library.
Christopher Trudeau RP Team on Sept. 21, 2023
You’re right, that was a miss. I’m not an Office guy, so it didn’t come to mind :)
Become a Member to join the conversation.
pb on Sept. 21, 2023
I’m surprised
__mifflin__
was notentioned :-)