Python: Unders and Dunders

June 3, 2017
4 min. read

This post is part of the Python Tips series.

There are a whole bunch of underscores in Python. If you are new to Python, this may seem weird. There is a reason behind the flatness. There are even special names for them, under when you have a single underscore and dunder when two underscores are used (for double underscore).

Unders

There are many uses of single underscores in Python. We will hit a few common and easy to explain right away and get them over with.

Naming

Python variable and function naming uses underscores to separate words. So a variable might be total_sales or number_of_semicolons. They should not be totalSales or numberOfSemicolons. Constants (which are just variables you don’t change) should be all caps and underscore separated like DATABSE_URL or FIREANTS_PER_POUND.

Functions should be exactly the same format as variable, such as save_humanity(method_to_use) or cause_destruction(method_to_use), depending on if you are a super hero or super villain.

Weak Internal Hiding

If you prefix your class variable or method, this is a signal to those using your class that you are expecting them to behave as adults and leave it be. Modifying it could cause issues. The user can still call the method or set the variable, but can take note of possibly being bad.

If you are using @property methods in a class, I like using the name of the property with an underscore prefix.

Collisions

When you must define a variable, method or function that is named the same as a Python keyword, it is common to use a trailing underscore. However, I find most situations can be handled better by being more descriptive instead. We type once and read many times. And good IDEs also allows auto complete.

Dunders

Strong Internal Hiding

We saw that we can tell uses of our class methods that they are off limits with a single underscore. However, we can assure that they can’t call them with double underscores. Ok, that is kind of a lie. You cannot call them directly, but if you understand how the name mangling works, you can make your call to it. This idea is that most people won’t do that. If they do, they deserve what they get.

Wanna know how name mangling works, so you can be bad too?

If you have a class named Bob, and a method named __weave(), calling Bob.__weave() will not work. However, Bob._Bob__weave() will work. But don’t do it unless you have a real reason.

“Magic Methods”

There are tons of magic methods in Python. These look a little weird when defining classes and modules, but allow a way of making your objects easy to use and simple for users. These are called dunders. The reason why they are wrapped with duoble underscores, is that you might want to use the name for a standard method.

Lets cover how many of these methods work.

Common Class Methods

__init__ - We already covered this one, which handles initialization of an new instance.

__new__ - This isn’t as common as __init__, but is called before and when you need to handle the creation of the instance.

__del__ - This is technically a destructor, but you should not use it. Structure you code so you don’t need it. Python is not a language built for destructors, like C++. __del__ really should be gone (in my opinion). This is called when the GC decides to clean up your object, not when it goes out of scope. Exceptions raised in __del__ are ignored. One place that might be an OK use is if you are cleaning up ctypes. Much better to use a context manager if possible.

Context Managers

__enter__ and __exit__ are required for creation of context managers. Look for an upcoming post on context managers.

Callable Instance

The __call__ method allows your instance to be called. This is different than __init__ which allows you to call your class and create and instance.

Representation

__str__ - String representation of the object.

__repr__ - Allows you to return a representation of the class in a string format. Will be used in a print if __str__ does not exist.

For iteration

__len__ - Provide implementation for the len() function.

__iter__ or __getitem__ - Returns item for iterator

__next__ - Gets next item in iterator.

__reversed__ - Allows reversing of an iterator

Comparison

Default comparison sometimes just works, but you can overload so that it works for the default data value of your object.

__eq__ - True if equal to other

__lt__ - True if self is less than other

Summary

Despite them often being called magic methods, think of them as just dunder methods and tools. If you use them properly, your objects and modules will just feel more Pythonic to use.

Hopefully this has demystified the underscores of Python. They are not magical, just features of the language.


Part 5 of 9 in the Python Tips series.

Series Start | Python: Mutable Defaults and Decorators | Python: Context Managers

comments powered by Disqus