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