Detailed Explanation of Python Magic Methods
What are Python Magic Methods

What are magic methods? Of course, they have nothing to do with magicians. They are everywhere in object-oriented Python. They are special methods that allow you to add “magic” to your classes. These methods are automatically called during certain operations and represent the wisdom of Python’s object-oriented design. For beginners, mastering Python’s magic methods becomes especially important.
In plainer terms, magic methods are the interface that lets your custom class behave more like a built-in Python type. Once you implement them, your objects can participate in initialization, comparison, printing, arithmetic, calling, and other language-level behaviors.
Magic methods are often named surrounded by two underscores. However, there isn’t great documentation explaining them clearly, and it’s hard to find their correct pronunciation. For example, init, do we call it “double underscore init double underscore”? It sounds awkward.
So what magic does init really have? The answer is you don’t usually call it directly. Instead, it is called automatically when instantiating a class. For example, when we use x = A(), the class A’s __new__ and __init__ methods are called automatically.
When Is It Worth Implementing Magic Methods?
In practice, you do not need to implement every magic method for every class. A simpler rule is: add them when you want your object to behave naturally in a specific Python context.
- If you want printed objects to be easier to read, implement
__str__or__repr__. - If you want objects to support equality or ordering, implement comparison methods.
- If you want objects to support operators like
+or-, implement operator overloading methods. - If you want an instance to behave like a callable function, implement
__call__.
Constructor and Initialization Magic Methods
We all know the most commonly used magic method is __init__, the initialization method declared in a class. But __init__ is not the first magic method to be used; before initialization, __new__ is called first. This method creates an instance first, then __init__ initializes the instance. In the lifecycle of an instance, __del__ is called last to delete the instance. Let’s first take a look at these three magic methods.
__new__(cls, [...)
__new__is the first method called in instantiating a type. After calling__new__, it passes to__init__. Although__new__is less commonly used, it has unique roles. For details, see the official Python documentation.
__init__(self, [...)
__init__ is the initialization function of a class instance. It takes parameters passed to the constructor (e.g., x = ClassA(10, 'foo')), so 10 and 'foo' become the initialization parameters. Almost every class defines this function.
__del__(self)
Both __new__ and __init__ create a new class instance, while __del__ is the destructor called before an instance is destroyed. It’s useful when the class needs extra cleanup before being deleted, such as closing sockets or files. Note that this function cannot guarantee to always run successfully, so good programming habits like explicitly closing files or network connections are important.
Below is an example using __init__ and __del__ magic methods:
from os.path import join
class FileObject:
'''Ensure opened files are closed.'''
def __init__(self, filepath='./', filename='sample.txt'):
# Open file in r+ mode
self.file = open(join(filepath, filename), 'r+')
def __del__(self):
self.file.close()
## Close file
del self.file
Defining Operator Usage for Your Class
A big advantage of magic methods is that they let you define your own classes to behave like built-in classes, avoiding a lot of ugly and non-standard code. In other languages, you might write:
if instance.equals(other_instance):
# do something
This can work in Python too, but Python has a more elegant way: __eq__.
def __eq__(self, other):
return self.value == other.value
One important detail here: the body of __eq__ should compare real attributes, not call self == other again, because that would trigger __eq__ recursively.
This is only one part of what magic methods can do. Most of them let us define the meaning of operators and behaviors so our own classes behave more like built-in ones.
Comparison Magic Methods
Python has a complete set of magic methods to define most comparison operations. We can even override the meaning of built-in functions.
| Operator | Magic Method |
|---|---|
| < | object.lt(self, other) |
| <= | object.le(self, other) |
| == | object.eq(self, other) |
| != | object.ne(self, other) |
| >= | object.ge(self, other) |
| > | object.gt(self, other) |
Numeric Operators
| Operator | Magic Method |
|---|---|
| + | object.add(self, other) |
| - | object.sub(self, other) |
| * | object.mul(self, other) |
| // | object.floordiv(self, other) |
| / | object.truediv(self, other) |
| % | object.mod(self, other) |
| ** | object.pow(self, other[, mod]) |
| « | object.lshift(self, other) |
| » | object.rshift(self, other) |
| & | object.and(self, other) |
| ^ | object.xor(self, other) |
In-place Operators
| Operator | Magic Method |
|---|---|
| += | object.iadd(self, other) |
| -= | object.isub(self, other) |
| *= | object.imul(self, other) |
| /= | object.idiv(self, other) |
| //= | object.ifloordiv(self, other) |
| %= | object.imod(self, other) |
| **= | object.ipow(self, other[, mod]) |
| «= | object.ilshift(self, other) |
| »= | object.irshift(self, other) |
| &= | object.iand(self, other) |
| ^= | object.ixor(self, other) |
| = |
Unary Operators
| Operator | Magic Method |
|---|---|
| - | object.neg(self) |
| + | object.pos(self) |
| abs() | object.abs(self) |
| ~ | object.invert(self) |
| complex() | object.complex(self) |
| int() | object.int(self) |
| long() | object.long(self) |
| float() | object.float(self) |
| oct() | object.oct(self) |
| hex() | object.hex(self) |
Magic Method Example
Here we define a Length class that can perform length calculations in different units.
class Length:
__metric = {"mm" : 0.001, "cm" : 0.01, "m" : 1, "km" : 1000,
"in" : 0.0254, "ft" : 0.3048, "yd" : 0.9144,
"mi" : 1609.344 }
def __init__(self, value, unit = "m" ):
self.value = value
self.unit = unit
def Converse2Metres(self):
return self.value * Length.__metric[self.unit]
def __add__(self, other):
l = self.Converse2Metres() + other.Converse2Metres()
return Length(l / Length.__metric[self.unit], self.unit )
def __str__(self):
return str(self.Converse2Metres())
def __repr__(self):
return "Length(" + str(self.value) + ", '" + self.unit + "')"
if __name__ == "__main__":
x = Length(4)
print(x)
y = eval(repr(x))
z = Length(4.5, "yd") + Length(1)
print(repr(z))
print(z)
Output:
4
Length(5.593613298337708, 'yd')
5.1148
The __call__ Method
Many times when writing Python you encounter the error "TypeError: 'xxx' object is not callable", meaning the class instance is not callable. Defining the __call__ method makes your custom class callable. Here is an example:
class Polynomial:
def __init__(self, *coefficients):
self.coefficients = coefficients[::-1]
def __call__(self, x):
res = 0
for index, coeff in enumerate(self.coefficients):
res += coeff * x** index
return res
# a constant function
p1 = Polynomial(42)
# a straight Line
p2 = Polynomial(0.75, 2)
# a third degree Polynomial
p3 = Polynomial(1, -0.5, 0.75, 2)
for i in range(1, 10):
print(i, p1(i), p2(i), p3(i))
Output:
2 42 3.5 9.5
3 42 4.25 26.75
4 42 5.0 61.0
5 42 5.75 118.25
6 42 6.5 204.5
7 42 7.25 325.75
8 42 8.0 488.0
9 42 8.75 697.25
References:
https://www.python-course.eu/python3_magic_methods.php
https://rszalski.github.io/magicmethods/
- 原文作者:春江暮客
- 原文链接:https://www.bobobk.com/en/194.html
- 版权声明:本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。