Have you ever wondered how you can run operations like adding (+
) two numbers or strings in Python and yet get reasonable results? In this article we will get to know the answer and a bit more.
Python Core Syntax¶
In Python we can run operations like 1+2
or 'a' + 'b'
and get results of 3
and ab
respectively. Or we can call functions like len()
on different data structures (List, Dictionary, Tuple, and Set) and get the total number of elements as shown in the code block below.
l1 = [1, 2, 5]
d1 = {"one": 1, "two":2}
t1 = (1, 2, 3)
s1 = {1, "two"}
[len(i) for i in [l1, d1, t1, s1]]
[3, 2, 3, 2]
These are some examples of Python core syntax. Some other types of common Python core syntax are:
- Common algebraic expressions e.g.
+
,-
,*
. - Common operators for comparison e.g.
==
,<
,in
. - Built-in functions e.g.
len()
,str()
. - Reflexion e.g.
type()
,isinstance()
etc.
Magic Methods¶
The reason that Python can understand these core syntaxes is that anytime Python receives a core syntax it can relate it to a specific method that's responsible for that specific syntax.
For example, when we call something like 1+2
, behind the scene, Python calls a method called __add__()
or when we call len()
, Python calls for __len__()
method. Such methods are called magic methods or special purpose methods. Checkout the following code block for a demo.
result1 = 'a' + 'b'
print(f"Summing up a, b with `+` = {result1}")
result2 = 'a'.__add__('b')
print(f"Summing up a, b with `__add__()` = {result2}")
Summing up a, b with `+` = ab Summing up a, b with `__add__()` = ab
Notice, how we used the magic function __add__()
using a dot notation. It's because __add__()
is a private method that's already implemented inside the string
class which is the class type of our objects: a
. We can also apply the dir()
function on a
to check out the other methods that are already implemented.
print(f"Class of `a` is = {'a'.__class__}")
print("\nAll the methods available in `a`: ")
print(dir('a'.__class__))
Class of `a` is = <class 'str'> All the methods available in `a`: ['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'removeprefix', 'removesuffix', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
Implementing Magic Methods in Custom Classes¶
The demonstration above shows how for Python to function properly on the core syntax, the magic method needs to be delivered by the classes of the objects involved in the operation. This also opens up the opportunity for us to implement magic methods in our custom classes.
⚠️ If you need a quick refresher on Python classes, check out my article OOP in Python - Understanding a Class.
For example, let's assume that we are working for a trucking company that carries cars. To provide a quick check on the total weights of their onboarded cars, we will implement __add__()
method. Surely, this is an overkill but complicating simple things is a great way to learn the basics!
# defining a super class
class Vehicle:
def __init__(self, kind, weight):
self.kind = kind
self.weight = weight
def __add__(self, other):
return self.weight + other.weight
mustang = Vehicle("car", 3532)
prius = Vehicle("car", 3010)
print(f"Total weight of mustang and prius: {mustang + prius}")
Total weight of mustang and prius: 6542
🛑 Notice, how the core syntaxes work only within the confines of the same object type - meaning you can run 'a' + 'b'
and get a result but calling 'a' + 5
will result in an error. They operate similarly but has a built-in check to ensure the operations are called with the same data types. Check the code block below:
try: 'a' + 5
except Exception as e:
print(e)
try: mustang + 10
except Exception as e:
print(e)
can only concatenate str (not "int") to str 'int' object has no attribute 'weight'
Also, notice the error differences thrown by our custom class instances vs an sring object while running the same method. Which tells us that while implementing such special methods, we should also implement a check to ensure the passed on object's data type is of expected type or else raise an exception.
Other Methods¶
We have already seen how to look into the implemented methods from a class by using dir()
function. We can also use help()
function to see the implementation of the methods provided that documentation is available for that class. For example, to look at the built in method of a string object we can run this: help('a'.__class__)
.
Running this command will print out all the methods and take a lot of space so I have added a screenshot with only a couple of methods.
In this article, we took a peek inside the world of python's core syntax. We talked about a couple of common core syntaxes and discussed about their corresponding magic methods. But there are a lot more magic methods that are already implemented in Python. Checkout special methods reference page from Python.org for a detail list of special methods.