Skip to main content

Getting started with classes in Python Part 8: "dunder"

james.derrick@ansys.com | 01.29.2025

We have already learnt about private and super-private naming, but what if I told you there's a secret third thing after all? Well, it turns out there actually is: the "dunder" name. "Dunder" means "double underscore" and refers to methods that have a double underscore as the prefix and suffix of their names. You are most likely to have seen this in code that contains the following snippet.

if __name__ == '__main__':
    main()

where main is just some function in the program, however, it's just the dunder variable __name__ that we're interested in here.

What does this mean for things with dunder names?

Dunder names are used to denote variables and functions that cover the inner-workings of Python. A lot of what goes on under the hood is described by dunder functions, variables and methods. __name__ is actually a very good example. This is a built-in variable that takes on different values depending on how the code it is in is being run.

  • When code is run as a script __name__ is set to '__main__', which is the name of the top-level environment of the program.
  • When code is imported as a module, __name__ is set to the name of the module file

These two conditions are used in the if statement above. In other words, if you run a python file as a script the if block gets called, but if you just wanted to re-use the functions and classes in it you can import the file as a module and the if block will not be triggered. Pretty neat!

How do dunder methods apply to classes?

When one performs various actions using classes in Python that don't explicitly use dot notation to call a method, a dunder method is likely being called in the background to do that thing. The best example of this is class instantiation and the __init__ method. When you create an instance of class, as we have in previous Parts, like below, the __init__ method is called when the class is created.

from dataclasses import dataclass


@dataclass
class Car:
    make: str

c = Car('VW')  # Car.__init__ is called here

dataclass hides this for us, but we can actually create the same class without it, using __init__.

class Car:
    def __init__(self, make: str):
        self.make = make
        print('__init__ called')

c = Car('VW')

These two code snippets are equivalent, except for the print I added to the second. If you don't believe me, try them out and see for yourself! Well, mostly equivalent. dataclass adds a bunch of additional things for us as well, including a human-readable string representation of the class when printing. In the second example we would have to manually add that ourselves using other dunders. Day-to-day we just don't need to bother and dataclass handles most of the common use cases without any trouble.

Another example of a dunder method on classes is the __add__ method which is called when the + operator is used on a class instance.

from dataclasses import dataclass


@dataclass
class Coordinate:
    x: int
    y: int

    def __add__(self, other):
        return Coordinate(self.x+other, self.y+other)

c = Coordinate(0, 0)
print(c + 2)

This prints Coordinate(x=2, y=2). There are dunders for all sorts of operations and behaviours in Python and messing with them can be dangerous. You don't really want to change things in ways you aren't fully confident with. For example, in the code just presented, you may have noticed that other can be any type and if it's a Coordinate it will fail. Perhaps it should, perhaps it shouldn't, but either way it is up to you as the implementer to make it do the 'correct' thing. Often it's best to leave well alone.

Conclusion

In this series on classes we have covered the majority of functionality you need to be familiar with to work well with classes. However, these last few articles are starting to stray into the territory of tips and tricks. You don't need to know many dunder methods and I haven't covered many here; there are a LOT. It is enough to know what they are and a bit about how they operate. They will be mentioned and referred to in the final few parts in this series a fair amount and you should be comfortable with them being discussed before tackling those parts.

Take a Step Back?

If you haven't already read the previous articles in this series, you may want to take a step back to look at some of the information that has brought us to this point. For a complete look at the articles in this series, you can visit the overview page: Getting started with classes in Python

Or, if you want to look directly at a particular previous article, here are the links:

  • Part 1: Anatomy of a Class
  • Part 2: dataclasses
  • Part 3: Methods and properties
  • Part 4: Hashing & Mutability
  • Part 5: classmethod and staticmethod
  • Part 6: Public, Private and Super-Private Naming
  • Part 7: getters and setters
  • Part 8: dunder methods