Decoding the Magic of Python

In the world of computers, software development has been one of the most important aspects to create applications, automate processes and make the job of the end user as easy as possible.

For example, consider an ATM machine. The software installed on the ATM machine helps the user to withdraw cash from a Bank account irrespective of the location of the Bank. For an end user who might not have any idea about the functioning or logic of the software, this looks like MAGIC.

One of the basic tenets of Magic is illusion, and that is exactly what a programming language helps us to achieve.

If I have to pick one programming language, which provides maximum magic methods or WOW moments, it has to be Python.

Note: Before Continuing with this section, please read\go through basics of Python from https://www.python.org/.

If we have to compare Magic with Python, we will find many similarities. Both provide multiple tricks, both create a sense of illusion, and both have magic methods. So, let’s discuss what kind of magic does Python really provide.

Ex 1:

One simple trick in python is reversing a string without using any built in methods.

Code:  list1 = [1,2,3,4,5]
 
list2= list1[::-1] ; print list2
 
Result:  [5,4,3,2,1]

Wow, Magic right? No, it’s just a simple slicing.

Explanation :

It’s the slice notation:

[start:stop:step]

  • startis the index where you start. If it’s omitted, Python assumes you want to start at the beginning.
  • stopis where you want to stop. If you omit it, Python assumes you want to go until the end.
  • stepis what the -1 is taking advantage of. 1 is the default value. 2 iterates over every other element. -1 iterates over all of the elements, but backwards

Ex 2: Decorators

Have you ever seen a magic trick, where a blank white paper is kept in a box and suddenly after reopening the box, the colour of this paper changes to Blue.

This kind of magic can be achieved with help of Decorators in Python.

Decorators allow you to make simple modifications to callable objects like functions, classes etc.

Code : @blue
 
def paper(args):
 
print “Paper”
 
… some operation
 
Result:  The paper will get decorated with blue colour.

Explanation:

The above @ operator helps the code to work as given below:

paper = blue(paper)  , where blue is another function which colours the underlying function as shown below.

def blue(args):

…. Code to colour the underlying object\function in blue colour

Hence, we can modify the functionality of the old function as per our requirement.

You can go ahead and read a bit more about decorators, but this is the basic functionality about the decorators.

Ex 3: Magic Methods:

Let’s talk about the magic again.

There are some magic tricks, where the magician makes a person disappear and suddenly the person appears at some other location. But, the real trick lies in the underlying objects or tools which helps the person to relocate within few seconds.

In python, there are some inbuilt methods, which are actually called Magic methods.

Python has some similar underlying methods, which are invoked behind the scenes and the user does not have to invoke them directly.

These methods are __init__ , __new__ and __del__ .

e.g. If we have a class named Person(), and as soon as we create an object of Person

P1 = Person(), the first method to be called in the background is __new__

Later the constructor function __init__ is called, which is the initialisation function.

Observe that, we just created an object P1 here and did not call the __init__ or __new__ functions explicitly, but still they were called. There lies the MAGIC.

All magic methods work in a similar way.

Operator Magic Methods (overloading):

We can do operator overloading in python, which means that operators like ‘+’, ‘-’ can be used to execute underlying methods like __add__.

The + operator can be used for adding numbers and at the same time to concatenate the strings. These operators are actually the methods defined in respective classes. Operator overloading is defining methods for operators.

For e.g. We can use the ‘+’  operator with custom objects  by defining a method called __add__( )  .

See below given example.

import math
 
 
 
class Create_Circle:
 
 
 
def __init__(self, r):
 
self.__r = r
 
 
 
def setRadius(self, r):
 
self.__r = r
 
 
 
def getRadius(self):
 
return self.__r
 
 
 
def area(self):
 
return math.pi * self.__r ** 2
 
 
 
def __add__(self, another_circle):
 
return Circle( self.__r + another_circle.__r )
 
 
 
c1 = Create_Circle(4)
 
print(c1.getRadius())
 
 
 
c2 = Create_Circle (5)
 
print(c2.getRadius())
 
 
 
c_new = c1 + c2 # Here, this is a result of overloaded + operator by adding a method named __add__()

print(c3.getRadius())

python has many other special methods similar to __add__ , see the list below. (List copied from python.org)

OPERATOR FUNCTION   METHOD DESCRIPTION
+   __add__(self, other)  Addition
*   __mul__(self, other)  Multiplication
  __sub__(self, other)  Subtraction
%   __mod__(self, other)  Remainder
/   __truediv__(self, other)  Division
<   __lt__(self, other)  Less than
<=   __le__(self, other)  Less than or equal to
==   __eq__(self, other)  Equal to
!=   __ne__(self, other)  Not equal to
>   __gt__(self, other) Greater than
>=   __ge__(self, other)  Greater than or equal to
[index]   __getitem__(self, index)  Index operator
in   __contains__(self, value) Check membership
len __len__(self)  The number of elements
str __str__(self)  The string representation

Controlling Attribute Access for Classes:

This section can be compared to the more complex real magic tricks like walking on water, making big objects like a truck disappear etc.

Python does not support creation of private variables\attributes by default. But, Python accomplishes a great deal of encapsulation through “magic”, instead of explicit modifiers for methods or fields.

The functions available for this functionality are __getattr__, __setattr__, __getattribute__

__getattr__ : Ths function is called, when user is trying to access an attribute that does not exist. We can handle AttributeError messages and return any new value.

e.g

<strong>class</strong> <strong>Test</strong>(object):    
<strong>def</strong> __init__(self):        
self.a = 'a'        
self.b = 'b'     
<strong>def</strong> __getattr__(self, name):        
<strong>return</strong> 9850 
t = Test()
<strong>print</strong> t.a
<strong>print</strong> t.b
<strong>print</strong> t.c

Expected output:

a
b
9850
9850
True If we look at the above given class, the attributes a and b were printed correctly, because these 2 attributes were declared in init function.But, c was not defined, hence the flow of the code went to the __getattr__ function and the value ‘9850’ got printed.

__setattr__:                                                                                                                      r

This function is called when you do a.x = 5, that’s done as a.__setattr__(‘x’, 5).

Hence, you don’t need to explicitly call the setattr method, but its invoked from the a.x assignment.

Code sample:

<blockquote>&gt;&gt; <strong>class</strong> <strong>Frob
</strong>:...     <strong>def</strong> <strong>__setattr__</strong>(self, name, value):
...         self<strong>.</strong>__dict__[name] <strong>=</strong> value<strong>.</strong>upper()
...
&gt;&gt;&gt; f <strong>=</strong> Frob()
&gt;&gt;&gt; f<strong>.</strong>attr1 <strong>=</strong> "sagar"
&gt;&gt;&gt; f<strong>.</strong>attr1
Result : 'SAGAR' 

If you observe the above given example, you would realise that, after setting the attr1 to the name ‘sagar’, the code flow called the setattr method. The method converted the string into uppercase and returned back SAGAR in caps. 

 __getattribute__ : __getattribute__ is invoked before looking for the attribute in the object instance. It has precedence over __getattr__.

The __getattr__ is invoked if the attribute wasn’t found the usual ways; while__getattribute__ is more like forcing the attribute value to be taken from the function.

If you have the __getattribute__ method in your class, python invokes this method for every attribute regardless whether it exists or not, as we can see in the below given example.It can be used to override an attribute in the class as well. (Code reused from stack overflow example)

e.g

&gt;&gt;&gt; <strong>class</strong> <strong>Test</strong>(object):
 
...     <strong>def</strong> <strong>__getattribute__</strong>(self, name):
 
...         print "getting `{}`"<strong>.</strong>format(str(name))
 
...         object<strong>.</strong>__getattribute__(self, name)
 
...
 
&gt;&gt;&gt; f <strong>=</strong> Test()
 
&gt;&gt;&gt; f<strong>.</strong>sagar <strong>=</strong> 10
 
&gt;&gt;&gt; f<strong>.</strong>sagar
 
getting `sagar`

In the above example, I created an attribute sagar with value 10.But, after calling f.sagar, the __getattribute__ was called and the value ‘getting sagar’ got precedence over the set value i.e 10 Just like real Magic tricks, there are multiple magic methods available in python.You can try out all the methods given above and explore new areas as well.

Sagar Naik

Sagar Naik

Technical Lead at Calsoft Inc.
Experienced Technical Lead with a demonstrated history of working in the computer software industry. Skilled in Test Automation, Test Management, Python, and Test Planning. Strong engineering professional graduated from Pune University.
Sagar Naik

Latest posts by Sagar Naik (see all)

Leave a Reply

Your email address will not be published. Required fields are marked *