Introduction
Python magic methods are a particular type of Python function whose specific purpose is to overload existing/built-in Python operators and/or methods.
Magic methods are defined using the __
syntax - that is the method name surrounded on each side by a double underscore. Such a syntax avoids the possibility that a programmer accidentally defines a method with the same name.
In the context of classes and object-oriented programning you can use (that is define) these magic methods such that they are called when the instances of the class are used in certain situations.
-
For example the
__eq__
method can be used to define under what situations its instances should be considered equivalent. And thus, if__eq__
is defined it will be invoked if the class meets an equality test using the==
operator. -
For example, when an instance of a class is instantiated the
__init__
method is invoked. -
The point is therefore that you are not actually calling these magic methods youself, directly. Instead, it is the Python interpreter that is calling them - because by default it knows the situations/conditions under which they should be run.
Therefore, magic methods provide Python with a consistent data model across both built-in and custom classes, and thus allowing vastly different data types to interact with each other in predictable ways.
Syntax
Magic method syntax follows a consistent pattern - with the name of the method being surrounded on both sides by two underscores.
Again, this convention exists to provide the programmer a certain amount of freedom when it comes to naming their own methods and variables … assuming you stay away from naming your own objects with double underscores(!).
Each magic method serves a specific purpose:
- For example, the
__init__
method is run whenever a new instance of a class is created.
class MyClass(object):
def __init__(self):
print("The __init__ method is running")
>>> my_class = MyClass()
The __init__ method is running
And again, the point to note here is that its not you who is actually calling these magic methods. Instead, it is the Python interpreter that is calling them - because by default it knows the situations/conditions under which they should be run. Although the naming/spelling of the magic methods, and their signature, varies from one to the next, generally each magic method works this way.
Available Magic Methods
Creation, Initialization, and Destruction
__new__
The purpose of __new__
is to actually create and return the instance of a class. It is the first magic method to be called when an object of a class is instantiated. It is then followed by __init__
.
Details:
-
The first argument to
__new__
is the class of which an instance is being created (and by convention this is calledcls
). -
Then, any remaining arguments to
__new__
should mirror the arguments to__init__
… which is to say that all arguments that are passed to the class instantiation call are first sent to__new__
(because it is called first) before then being sent to__init__
(for initialization and customization of the instance). -
Usually however, you don’t actually need to define, nor explicitly call,
__new__
. Python’s built-in implementation is adequate. -
If a class does define
__new__
its usually because the class to reference the implementation of some superclass first. And then whatever custom functionality the current class provides is then implemented.
__init__
This magic method is run just after when a class instance is created … the __init__
method of an objet runs immediately after the instance is created. It requires one positional argument (self
), after which it can then take any number of positional arguments (both required and optional) and keyword arguments. This flexibility in the method signature is required because all arguments that are passed to the class instantiation call are also sent to __init__
.
The purpose of __init__
is to provide initial data to the object after is has been created. That is, to customize the instance once it has been created. Therefore, in practice, __init__
does not (and should not) actually reaturn anything.
All __init__
methods return None
… returning anything else will raise a TypeError
. The most appropriate use of __init__
is for instantiating objects, which are created by a custom class, such that the extra variables can customize their implementation in some way.
Example … rolling a dice:
import random
class Dice(object):
"""A class representing a dice with an arbitrary number of sides"""
def __init__(self, sides=6):
self._sides = sides
def roll(self):
return random.randint(1, self._sides)
>>>standard_die = Dice()
>>>dodecahedron = Dice(sides=12)
>>>
>>>standard_die.roll()
5
__del__
The __del__
method is invoked when an object is being destroyed. The __del__
method is run regardless of how an object comes to be destroyed, whether through direct deletion invoked in ode or through memory reclamation and automatically triggered by the garbage collector. Indeed Python’s memory management is good enough that you should usually simply allow the garbage collector to delete objects that are no longer in scope and/or needed. However, if for whatever reason you need to explicitly destroy an object then the __del__
method is how this should be done.
Type conversions
__str__
- The most commonly used type conversion magic method is
__str__
. - This method takes a single positional argument (
self
) and is invoked when an object is passed to thestr
constructor and is expected to return a string. It will also be invoked if the%s
formatting expression is encountered.
class MyClass(object):
def __str__(self):
return "Hello World"
>>> str(MyClass())
'Hello World'
__int__
, __float__
, __bool__
, and __complex__
- These magic methods allow complex objects to be converted to more primitive numerical representations.
- A representative example would be if an object defines an
__int__
method, which should return anint
, then it will be invoked if the object is passed to theint
constructor.
Comparisons
__eq__
The __eq__
method requires the self
and other
arguments:
- Thus we have the instance of the object (the
self
) and the other object against which it is being compared (theother
). - It is run when Python is asked to make an equivalence check with the
==
operator $\dots$ withother
being set to the object on the other side of the==
.
class MyClass(object):
def __eq__(self, other):
return type(self) == type(other)
>>> my_class_A = MyClass()
>>> my_class_B = MyClass()
# Ask Python to make an equivalence check, using the == operator:
>>> my_class_A == my_class_B
True
>>> MyClass() == "hello"
False
__lt__
, __le__
, __gt__
, and __ge__
As you’d expect these map to their respective operators.
Each takes two arguments - self
and other
- and return True
if the relative comparison should be considered to hold, and False
otherwise.
Operator Overloading
- All Python’s binary operators can be overloaded within class objects.
- Python actually provides three magic methods for each operator, each of which takes two positional arguments, which by convention are
self
andother
. - The vanilla method: behaving in the manner you’d expect and simply returning the result.
- The reverse method: where the operand are swapped.
- The in-place method: that are called when the operators that modify the former variable in place are used.
Conclusions
There is no reason to require that every custom class implement all magic methods … or indeed any of them.
When writing a custom class, consider what functionality you need and if that functionality maps cleanly to an existing magic method it is preferable to implement that magic method rather than provide your own custom implementation.