Magic methods are special methods which can be defined (or already designed and available) to act on objects.
Magic methods start and end with underscores
"__", and are not implicitly called by the user even though they can be. Most magic methods are used as syntactic sugar by binding it to more clear/easy_to_understand keywords.
Python is mostly objects and method calls done on objects. Many available functions in Python are actually tied to magic methods. Let’s checkout a few examples.
In : my_var = "Hello!" In : print(my_var) Hello! In : my_var.__repr__() Out: "'Hello!'"
As we can see, the
__repr__() magic method can be called to print the object, ie.. it is bound to the
This is true for many other builtin keywords/operators as well.
In : my_var = "Hello, " In : my_var1 = "How are you?" In : my_var + my_var1 Out: 'Hello, How are you?' In : my_var.__add__(my_var1) Out: 'Hello, How are you?'
Here, Python interprets the
+ sign as a mapping to the magic method
__add__(), and calls it on the L-value (Left hand object value)
my_var, with the R-value (Right hand object value) as the argument.
When a builtin function is called on an object, in many cases it is mapped to the magic method.
In : my_list_1 = ['a', 'b', 'c', 'd'] In : 'a' in my_list_1 Out: True In : my_list_1.__contains__("a") Out: True
in builtin is mapped to the
The methods available for an object should mostly be dependent on the type of the object.
In : my_num = 1 In : type(my_num) Out: int In : my_num.__doc__ Out: Out: "int(x=0) -> int or long\nint(x, base=10) -> int or long\n\nConvert a number or string to an integer, or return 0 if no arguments\nare given. ....>>> In : help(my_num) class int(object) | int(x=0) -> int or long | int(x, base=10) -> int or long | | Convert a number or string to an integer, or return 0 if no arguments | are given. If x is floating point, the conversion truncates towards zero. | If x is outside the integer range, the function returns a long instead.
From the tests above, we can understand that the
help() function is actually mapped to the
object.__doc__ magic method. It’s the same doc string that __doc__ and help() uses.
NOTE: Due to the syntax conversion (
__add__(),and other conversions), operators like
in, etc.. are also called Syntactic sugar.
What is Syntactic sugar?
According to Wikipedia, Syntact sugar is:
In computer science, syntactic sugar is syntax within a programming language that is designed to make things easier to read or to express. It makes the language “sweeter” for human use: things can be expressed more clearly, more concisely, or in an alternative style that some may prefer.
Hence, magic methods can be said to be Syntactic sugar. But it’s not just magic methods that are mapped to syntactic sugar methods, but higher order features such as Decorators are as well.
def my_decorator(my_function): def inner_decorator(): print("This happened before!") my_function() print("This happens after ") print("This happened at the end!") return inner_decorator def my_decorated(): print("This happened!") var = my_decorator(my_decorated) if __name__ == '__main__': var()
The example above borrows from one of the examples in the post on Decorators.
my_decorator() is a decorator and is used to decorate
my_decorated(). But rather than calling the decorator function
my_decorator() with the argument
my_decorated(), the above code can be syntactically sugar-coated as below:
def my_decorator(my_function): def inner_decorator(): print("This happened before!") my_function() print("This happens after ") print("This happened at the end!") return inner_decorator @my_decorator def my_decorated(): print("This happened!") if __name__ == '__main__': my_decorated()
Observing both code snippets, the decorator is syntactically sugar coated and called as:
instead of instantiating the decorator with the function to be decorated as an argument, ie..
var = my_decorator(my_decorated)
A few syntax resolution methods:
- ‘name’ in my_list -> my_list.__contains__(‘name’)
- len(my_list) -> my_list.__len__()
- print(my_list) -> my_list.__repr__()
- my_list == “value” -> my_list.__eq__(“value”)
- my_list -> my_list.__getitem__(5)
- my_list[5:10] -> my_list.__getslice__(5, 10)
NOTE: This article is written from the notes created while learning magic methods. The following articles (along with several others) were referred as part of the process.