In growing the Python concept map, I thought I’d take today to cover the concept of special methods, as their called in the documentation. However, you may have heard them called magic methods or even dunder methods. In any case, that’s what we’ll be covering today!
Table of Contents
- Concept Overview
- Why Do Special Methods Exist?
- What Special Methods Should I Care About?
- Learning About Special Syntax
Concept Overview
If you’ve been playing with Python for any length of time, there’s a good chance you’ve come across a few methods with some strange syntax. After all, the standard for methods (and naming broadly) is snake case, where lowercase words are separated by underscores (e.g., my_function()
, my_variable
, my_method()
).
However, there are a few methods, which we call special methods (or sometimes magic methods or dunder methods), that don’t obey that convention. Instead, they start and end with pairs of underscores. For example, you might have made an object of your own and been tasked with implementing the __init__()
method.
As it turns out, there are several of these special methods that each serve their own purposes:
__new__()
__init__()
__del__()
__repr__()
__str__()
__bytes__()
__format__()
__hash__()
__bool__()
By no means is this list exhaustive. In fact, there are even a few for when you want to define the relational operators (e.g., <
, >
, etc.) or arithmetic operators (e.g., +
, -
, etc.). Likewise, if you want to customize your objects even more, there are special methods like __iter__()
, __contains__()
, and __len__()
.
Ultimately, however, you probably have some questions about these special methods. In the rest of this article, I’ll try to answer them.
Why Do Special Methods Exist?
As you are probably already aware, Python has a variety of built-in functions, which provide convenience for many of the built-in data types like strings and lists. For example, we can use the built-in len()
function to get the length of a list. Similarly, we can use the built-in str()
function to get a string representation of that same list.
As it turns out, Python also has a lot of operators to allow for a variety of convenience features on those same pieces of data. For example, you can concatenate strings with the +
operator. You can also add together numbers with the same operator.
What makes this all possible are the special methods. See, it would be somewhat ridiculous to expect the built-in functions and operators to be able to work on any data type. It’s not like the underlying code for the +
operator has a special case for numbers, lists, and strings. Instead, each data type defines how that operator will behave through the corresponding special methods.
Just for the sake of argument, imagine you’re designing the list class in Python. You might write something like this to enable concatenation with the +
operator:
class list(): def __add__(self, other): self.extend(other)
Then, when the +
operator is used between two lists, this method is called and the first list is extended by the other. Of course, this isn’t how the actual +
operator works for lists, but you could see how you might go about implementing the feature yourself.
Seeing __add__()
in action, you might be wondering which special methods are even important. We’ll cover that question next.
What Special Methods Should I Care About?
The short answer to this question is to think about which built-in functions and operators you use most often and prioritize understanding them. However, here are some of my recommendations.
Constructing Objects With __init__()
By far the most important special method to know about is __init__()
because it serves as the constructor. Here, you can define as many parameters as you’d like for the purpose of instantiating an object. For example, if you want to create a Cat
class, you might give the user the option to specify a few features of the cat such as age, color, and name.
class Cat(): def __init__(self, age, color, name): self.age = age self.color = color self.name = name
It’s also okay to leave the constructor as-is. In that case, you will probably want to set your variables to some default value. Then, you can let the user control them through methods. That said, a constructor is usually a bit more convenient for your users.
Note that there are no built-in functions or operators that activate the __init__()
method. Instead, this method is called when you create an instance of the Cat class (e.g., Cat(7, "gray", "Reina")
).
Converting Objects to Strings With __str__() and __repr__()
Perhaps my favorite pair of built-in functions are str()
and repr()
because they allow us to convert any data type we want to a string. Of course, none of this is possible without having the corresponding special methods defined in our objects. As a result, if we want str()
and repr()
to return something useful, we need to implement __str__()
and __repr__()
.
I’ve written about the differences between these two functions somewhat extensively, but to summarize: str()
should return a client-facing string while repr()
should return a developer-facing string. For example, if you had an object for rolling dice, you might want str()
to return the total of the current roll (e.g., "8"
) while repr()
returns the underlying values like the number of sides on each die as well as their current state (e.g., "Dice(sides=[6, 6, 6], state=[3, 4, 1])"
).
Comparing Two Objects With __eq__()
While you can implement all of the relational operators for every class, the one relational operator I recommend implementing is ==
, which is computed through __eq__()
. The reason is somewhat obviously: if you ever want to verify that two objects are equal, you must implement __eq__()
. Otherwise, Python uses the is
operator, which only compares object IDs.
One thing to note is that you’re not required to implement !=
through __ne__()
. Fortunately, __ne__()
defers to __eq__()
. If you plan to implement the other relational operators, __eq__()
has no effect.
Learning About Special Syntax
Overall, Python has a lot of special methods. I didn’t count them all, but you might just look at the list of numeric special methods. This list contains methods for just about every math operation you might want to do from basic arithmetic to bitwise computation. In other words, you could really get lost in the weeds learning about the special methods—which are a set of features I’ve even considered abusing for fun.
With that said, I think this will have to do for today! If you liked this, I always recommend that you keep browsing as it helps the site grow:
- Speed Up Your Data Structures With Hashing
- 8 Coolest Python Programming Language Features
- 5 Things You Should Know Before You Pick Up Python
Likewise, if you want some more formal resources for learning Python, check out these books (#ad):
- Effective Python: 90 Specific Ways to Write Better Python
- Python Tricks: A Buffet of Awesome Python Features
- Python Programming: An Introduction to Computer Science
Finally, you can take your support even further by checking out my list of ways to grow the site, which includes links to my Patreon, Newsletter, and YouTube channel.
Recent Code Posts
In the world of programming languages, expressions are an interesting concept that folks tend to implicitly understand but might not be able to define. As a result, I figured I'd take a crack at...
It might seem like a straightforward concept, but variables are more interesting than you think. In an effort to expand our concept map, we're here to cover one of the most basic programming...