Sunday, 5 August 2012

Magic Methods in Python


When working with python, there are lot of things that make it interesting for users. It is made so with all the things python has to offer through its built in methods and others. In this blog I aim to succinctly explain these methods through simple syntaxes which would help better understand these methods.



Constructs and Initialisation

 During object instantiation __new__ is the first method to get called which ,with class and other arguments passes them to __init__. The syntax and usage is as follows:-


def __new__(cls,word):  #__new__ is declared in cases of strings as strings are                                                immutable.


def __init__(self,str): #__init__ is initialiser for the class. Used in python class
    self.str = 'hello'     definitions.


def __del__(self):       #__del__ is a destructor. It defines behavior for an object
  self.file.close()      when it is garbage collected.
del self.file 


Operator working 

In python the magic method make python behave like like simple built in types.
The following are the methods used in comparisons and numeric methods.


  def __eq__(self, other):          # compares if a given string and another 
    return len(self) == len(other)   string are equal in length.

  def __gt__(self, other):            # compares if a given string is greater
    return len(self) > len(other)        in length than another string. 
 
   def __lt__(self, other):           # compares if a given string is lesser in
    return len(self) < len(other)       length than another string.
 
 
  def __ge__(self, other):            # compares if a given string is greater than
    return len(self) >= len(other)      or equal to another string.
 
 
  def __le__(self, other):            # compares if a given string is greater than
    return len(self) <= len(other)    or equal to another string. 
 
 

Unary operators

def __pos__(self):        # Implements behavior for unary positive (e.g. +ve a)     
def __neg__(self):           # Implements behavior for negation (e.g. -ve a )
def __abs__(self):        # Implements behavior for the built in abs function. 
def __invert__(self):    # Implements behavior for inversion using the tilda operator. 


Arithmetic operators

def __add__(self, other):      #Implements addition.
def __sub__(self, other):      #Implements subtraction.
def __mul__(self, other):    #Implements multiplication.
def __floordiv__(self,other):#Implements integer division using its operator(double backslash).
def __div__(self,other):      #Implements division using its operator(backslash).
def __truediv__(self,other): #Implements true division. 
                                             Note that this only works   when from __future__ import 
                                 division is in effect.
def __mod_(self, other):           #Implements modulo using the '%' operator.
def __divmod__(self, other):     #Implements behavior for long division using this
                                                                    built in function.  
__pow__                                  #Implements behavior for exponents using the '**' 
                                                              operator.
def __lshift__(self, other):  #Implements left bitwise shift using the '<<' operator.
def __rshift__(self, other): #Implements right bitwise shift using the '>>'operator.
def __and__(self, other):        #Implements bitwise and using the '&' operator.
def __or__(self, other):         #Implements bitwise or using the '|' operator.
def __xor__(self, other):       #Implements bitwise xor using the '^' operator.

Augmented assignment

It is nothing but simple short hand assignment in C, assigned as
x=5
x+=1
All arithmetic operations can be implemented using __iadd__, __isub__,__imul__ etc.

Classes are represented for strings __str__(self), __repr__(self). __nonzero__(self) returns a true or false value.  


Controlling attributes

The controlling attributes can be explained using the following examples.


__getattr__(self, attr)
class Foo(object):
def __getattr__(self, attr):
print "looking up", attr
value = 42
self.__dict__[attr] = value
return value
f = Foo()
print f.x
=>looking up x             
42                # returns the value assigned.
 
 __setattr__(self, name, value)
def __setattr__(self, name, value):
    self.__dict__[name] = value       # assigns value to the dictionary.
 
__delattr__(self,name)
 
def __delattr__(self, name):
    if name == 'value':
           super(AccessCounter, self).__setattr__('counter', self.counter + 1)
     super(AccessCounter, self).__delattr__(name)] 
# deletes the attribute. But careful to avoid infinite recursion.



Magic Method for containers

Containers are anything that hold a value. They are accessed to get values, implements certain actions on them etc. The methods are discussed with help of examples as follows:-


def __len__(self):           #returns the length of the container.
   return len(self.values)
 
def __getitem__(self, key):   #returns the value indexed by the key in the
  return self.values[key]        container.
  
 
def __setitem__(self, key, value): 
   self.values[key] = value

def __delitem__(self, key):
   del self.values[key]

def __iter__(self):          #returns an iterator for the container.

   return iter(self.values)

def __reversed__(self):         #returns a reversed version of the list.

   return reversed(self.values)

def append(self, value):        #appends the value to the container.
   self.values.append(value)
def head(self):                 #returns the first element of the container.
   return self.values[0]
def tail(self):               #returns all the elements except the first ,of the 
 return self.values[1:]           container.
   
def init(self):               #returns elements till the last container.
   return self.values[:-1]
def last(self):               #returns the last element of the container.
   return self.values[-1]
def drop(self, n):            #returns the element except the first n.
   return self.values[n:]
def take(self, n):            #returns the first n elements.
   return self.values[:n]

  Callable Objects

Python allows functions to be passed as objects. This is a very powerful technique, which makes instances of classes callable like functions. __call__(self, [args...]) takes up variable number of arguments. An implementation of __call__ is given below.

class Factorial:
    def __init__( self ):
        self.cache= {}
    def __call__( self, n ):
        if n not in self.cache: 
            self.cache[n] = n*self.__call__( n-1 )
        return self.cache[n]

fact = Factorial()

  Context Managers

Context managers allow setup and cleanup actions to be taken for objects when their creation is wrapped with a statement. The behavior of the context manager is determined by two magic methods, which are explained in the example below.


class Foo(object):
    def __init__(self):
        pass
    def __enter__(self):
        print "I"
    def __exit__(self, type, value, traceback):
        print "snowhite"
 
 
results in I followed by snowhite.
 

Pickling

According to Wikipedia pickling is the standard method for object serialization. It uses a simple stack based virtual machine for serialization that uses the instructions for object reconstruction. It is not just for built-in but also for user defined types. Pickling is explained through an example in the following portion.
import pickle
favorite_color = { "lion": "yellow", "kitty": "red" }
pickle.dump( favorite_color, open( "save.p", "wb" ) ) # saves the dictionary to a file save.p.
favorite_color = pickle.load( open( "save.p", "rb" ) )# save.p has the dictionary favorite_color in it.
  
 
Python is well documented which provides many methods to help programmers work with ease. 




 

No comments:

Post a Comment