Python magic gets

 

__getattribute__ and __getattr__ and __get__ and __getitem__

Lets get into these many gets.

__getattr__

if the lookup attribute does not available in instance’s __dict__, then it will call __getattr__, still if it does not get, raises an AttributeError

The below example will make you clear(in python 2.x)

In [5]: class Test(object):
   ...:     def __init__(self):
   ...:         self.a = 1
   ...:         self.b = 2
   ...:     def __getattr__(self, att):
   ...:         print self.__dict__
   ...:         print "Calling getattr"
   ...:         return 0
   ...:     

In [6]: t = Test()

In [7]: t.__dict__
Out[7]: {'a': 1, 'b': 2}

In [8]: t.a
Out[8]: 1

In [9]: t.c
{'a': 1, 'b': 2}
Calling getattr
Out[9]: 0

In [10]: t.b
Out[10]: 2

__getitem__

If you try to access any data with square bracket of the instance, directly it calls __getitem__ method

This below one is one of the beautiful example which is taken from some source(in python 2.x):

In [4]: class Test(object):
   ...:     def __getitem__(self, items):
   ...:         print '%-15s  %s' % (type(items), items)
   ...: 
   ...: t = Test()
   ...: t[1]
   ...: t['hello world']
   ...: t[1, 'b', 3.0]
   ...: t[5:200:10]
   ...: t['a':'z':3]
   ...: t[object()]

<type 'int'>     1
<type 'str'>     hello world
<type 'tuple'>   (1, 'b', 3.0)
<type 'slice'>   slice(5, 200, 10)
<type 'slice'>   slice('a', 'z', 3)
<type 'object'>  <object object at 0x7f9222469e20>

__get__

getter, setter methods are important in oops world to encapsulate the data through these methods. Python provides same accessibility via property. So, the method-attribute can behave as a data-attribute to the instance via property. This is one of the powerful feature to control the data value without much change to the outer world.

So, In the below example, the temperature is just an attribute to the outer world for the class Celsius and with help of property concept, the business logic is encapsulated over temperature without any interference to the outer world

class Celsius:
    def __init__(self, temperature = 0):
        self._temperature = temperature

    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32

    @property
    def temperature(self):
        print("Getting value")
        return self._temperature

    @temperature.setter
    def temperature(self, value):
        if value < -273:
            raise ValueError("Temperature below -273 is not possible")
        print("Setting value")
        self._temperature = value
        
In [117]: c = Celsius()

In [118]: c.temperature
Getting value
Out[118]: 0

In [119]: c.temperature = -300
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-119-6dba9f781849> in <module>()
----> 1 c.temperature = -300

<ipython-input-116-5086c5ea8861> in temperature(self, value)
     14     def temperature(self, value):
     15         if value < -273:
---> 16             raise ValueError("Temperature below -273 is not possible")
     17         print("Setting value")
     18         self._temperature = value

ValueError: Temperature below -273 is not possible

In [120]: c.temperature = 30
Setting value

In [121]: c.temperature
Getting value
Out[121]: 30

__getattribute__

This method will get called always, if you try to access any attribute, if it could not find then it will throw AttributeError, then it does call to __getattr__ method. So, if you want to keep any security access to any type of attribute this is the right method to cover it up.

Nice example in python 3.x:

In [106]: class Count:
     ...: 
     ...:     def __init__(self,mymin,mymax):
     ...:         self.mymin=mymin
     ...:         self.mymax=mymax
     ...:         self.current=None
     ...: 
     ...:     def __getattr__(self, item):
     ...:             self.__dict__[item]=0
     ...:             return 0
     ...: 
     ...:     def __getattribute__(self, item):
     ...:         print("calling", item)
     ...:         if item.startswith('cur'):
     ...:             raise AttributeError
     ...:         return object.__getattribute__(self,item)
     ...:         # or you can use ---return super().__getattribute__(item)
     ...: 
     ...: obj1 = Count(1,10)
     ...: print(obj1.mymin)
     ...: print(obj1.mymax)
     ...: print(obj1.current)
     ...: 
calling mymin
1
calling mymax
10
calling current
calling __dict__
0

It is very overview about these magic methods in python. Still, lot more to explore to get nuance details about usage.

 

Advertisements

About Navaneethan

mixed feelings...
This entry was posted in 10min, Python and tagged , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s