##  [Getting started with classes in Python Part 8: "dunder"](/blog/getting-started-classes-python-part-8-dunder) 

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.

```python
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.

```python
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__`.

```python
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.

```python
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](https://developer.ansys.com/blog/getting-started-classes-python)

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

- [Part 1](https://developer.ansys.com/blog/getting-started-classes-python-part-1-anatomy-class): Anatomy of a Class
- [Part 2](https://developer.ansys.com/blog/getting-started-classes-python-part-2-dataclasses-0): `dataclasses`
- [Part 3](https://developer.ansys.com/blog/getting-started-classes-python-part-3-methods-and-properties): Methods and properties
- [Part 4](https://developer.ansys.com/blog/getting-started-classes-python-part-4-hashing-mutability): Hashing &amp; Mutability
- [Part 5](https://developer.ansys.com/blog/getting-started-classes-python-part-5-classmethod-and-staticmethod): `classmethod` and `staticmethod`
- [Part 6](https://developer.ansys.com/blog/getting-started-classes-python-part-6-public-private-and-super-private-naming): Public, Private and Super-Private Naming
- [Part 7](https://developer.ansys.com/blog/getting-started-classes-python-part-7-getters-and-setters): getters and setters
- [Part 8](https://developer.ansys.com/blog/getting-started-classes-python-part-8-dunder): dunder methods