DEV Community

Moshe Uminer
Moshe Uminer

Posted on

Dunder Methods

Let us begin with an introduction to dunder methods, a foundation for everything python.

The Magical Python

Much of python is magical, though one may only realize after using another programming language. Consider the following code:

# make a list
lst = [0, 1, 2, 3]
# get the length of the list
len(lst)

# 4

Nothing strange about that, right? But in most common languages, you would do something like this:

// make a list
let lst = [0, 1, 2, 3]
// get the length of the list
lst.length

// 4

The big difference is, that in other languages, length is an attribute of the list, which fits conceptually. But python makes len(lst) a separate function. Why?

The answer to this question is the key to advanced python programming: in python, the developer has the power to create classes that can be treated as built-in objects like lists. This can be done by implementing methods that are meant to be called by python not by python code.

To demonstrate this, let's write a class that will support the len() function.

A Stack

Let's take a look at the humble list:

lst = []
lst.append(0)
lst.append(1)
lst.append(2)

print(lst.pop())
# 2

print(lst.pop())
# 1

print(lst.pop())
# 0

As we can see, a python list has an append method to add to the end of the list, and pop to remove and return the last value in the list. But lists have other methods as well, we can access any item in the list, regardless of whether it is at the end:

lst = [0, 1, 2, 3]
print(lst[2])

# 2

So, let's say that I need to be able to add values a list (append), and get back the most recently added value (pop). But I'm also concerned that no one accidentally retrieves any other value from the list. In this case, we can write a class that will only allow for pushing values onto the end of the list, and to pop them off. In computer science, this is known as a stack.

class Stack:
    def __init__(self):
        self._stack = []

    def append(self, item):
        self._stack.append(item)

    def pop(self):
        return self._stack.pop()

Now we can use the Stack:

stack = Stack()
stack.append(0)
stack.append(1)
stack.append(2)

stack.pop()
# 2
stack.pop()
# 1
stack.pop()
# 0

Now we have a python list, that only has some of the methods of a list. But the methods that it does have are identical to a regular list, so we can simply swap one for the other (as long as no further methods are needed).

But there is a pitfall: what if at some point we need to check how many items are in the stack? We could take the naive approach, and add a length method:

class Stack:
    def __init__(self):
        self._stack = []

    def append(self, item):
        self._stack.append(item)

    def pop(self):
        return self._stack.pop()

    def length(self):
        return len(self._stack)

But then we lose the advantage we had before: that we can use the Stack and list the same way.

The pythonic approach would be the following:

class Stack:
    def __init__(self):
        self._stack = []

    def append(self, item):
        self._stack.append(item)

    def pop(self):
        return self._stack.pop()

    # The following method is a `dunder` or `magic` method
    def __len__(self):
        return len(self._stack)

Now we can do this:

stack = Stack()
stack.append(0)
stack.append(1)
stack.append(2)

print(len(stack))
# 3

Just like a regular list.

What is Going On?

When len() is called, what it does is more or less the following:

def len(obj):
    return obj.__len__()

This is how all dunder methods work: they are called by python in response to something the developer wrote.

By using a function (len) to call the method (__len__), instead of just calling the method itself, developer will tend to use the same method names as everyone else. In the case of length, every class that allows you checking its size, will use __len__, because all python developers expect to be able to use len, which only works with __len__.

Up Next

Next we will talk about how dunder methods power python's for loops, using sequences and iterators.

Further reading:

Oldest comments (0)