Dunder variables

Trey Hunner smiling in a t-shirt against a yellow wall
Trey Hunner
3 minute read Works on Python 3.7—3.10

Have you ever seen a variable in Python that has two underscores on either side of it (__like_this__)?

These are officially referred to as special attributes or special methods, but many Python programmers refer to them as dunder variables or dunder methods. That word dunder stands for "double underscore" because these variables are always surrounded by two underscores (__dunder__).

Dunder variables define a contract between Python programmers and the Python interpreter.

Dunder variables convey information

Python uses dunder variables for two general purposes:

  1. To convey information (often a form of metadata) to Python programmers
  2. To empower Python programmers to convey information to the Python interpreter

That's extremely hand-wavy. What does "convey information" really mean?

Let's look at some examples.

Python conveys information to us through dunder variables

Python stores docstrings in a __doc__ attribute:

>>> import math
>>> math.__doc__
'This module provides access to the mathematical functions\ndefined by the C standard.'
>>> math.sqrt.__doc__
'Return the square root of x.'

And Python stores the names of classes in a __name__ attribute:

>>> def what(thing):
...     print("That's a", type(thing).__name__)
>>> what("[1, 2]")
That's a str
>>> what([1, 2])
That's a list

In fact, modules and functions have a __name__ attribute too. You'll sometimes see a __name__ check at the bottom of a Python script to ask the question "is this module the entry point to our Python process"?

Python uses many dunder variables to convey information about objects to us (and to convey information back to itself).

Below are a few common dunder variables you might want to access yourself.

Classes use these dunder attributes:

  • __name__: stores their name
  • __dict__: stores their attributes
  • __module__: stores the name of the module they were defined in within
  • __bases__: stores their base classes (see inheritance)
  • __mro__: stores their method resolution order

Functions use these dunder attributes:

  • __name__: stores their name
  • __dict__: stores their attributes
  • __module__: stores the name of the module they were defined in within
  • __defaults__ and __kwdefaults__: store their default argument values

Modules use these dunder attributes:

  • __name__: stores their name
  • __dict__: stores their attributes
  • __file__: the file this module was loaded from (though some modules are missing this)

All objects use these dunder attributes:

  • __class__: stores the class of the object attribute
  • __dict__: stores their attributes (well, usually)

We can convey information to Python as well

As Python programmers, we can also use dunder variables to convey information to Python.

This is usually done with dunder methods when making a class.

The most common dunder methods to customize are:

Dunder methods are for us to define, but not for us to call.

If we want to see the programmer-readable string representation of an object, we could use the built-in repr function:

>>> n = 2
>>> repr(n)

Python uses the __repr__ under the hood for this.

>>> n = 2
>>> n.__repr__()

But we shouldn't call __repr__ ourselves: that's Python's job, not ours.

There are other cases where dunder variables are used for conveying information to Python, but they're far less common than dunder methods. One example is the module-level __getattr__ method. Another is the class-level __slots__ attribute.


Dunder variables, attributes, and methods have two purposes:

  1. Convey information to Python programmers: we're allowed to read these dunder variables
  2. Convey information to the Python interpreter: we're only supposed to write to these dunder variables

Dunder methods are typically for us to convey information to Python. Other dunder variables are sometimes for Python to convey information to us.

Write more Pythonic code

Need to fill-in gaps in your Python skills? I send regular emails designed to do just that.