Python Scipy sparse matrices explained
2021-04-28
Introduction to advanced candlesticks in finance: tick bars, dollar bars, volume bars, and imbalance bars
2021-06-12
Show all

Python __getattr__ and __getattribute__ magic methods

5 mins read

Python Magic Methods

Magic Methods are a broad and general term that refers to “special” methods in a Python class. There is no single definition for all of them, as their use is diverse. For example, a few common and well-known magic methods include:

  • __init__ that serves as the object initializer (sometimes incorrectly referred to as constructor)
  • __str__ that provides a “string representation” of your object
  • __add__ that allows you to “overload” the + operator.

What do all these methods have in common? Well, obviously they all start and end with double underscores (__). But aside from that, what makes them “magic methods” is that they’re invoked somehow “specially”. We don’t manually invoke these methods; Python is the one doing it. For example, we don’t do obj.__str__(), we do str(obj).

There are many magic methods, but as we will focus on __getattr__ and __getattribute__ in this post.

__getattr__

Let’s start with __getattr__. This method will allow you to “catch” references to attributes that don’t exist in your object. Let’s see a simple example to understand how it works:

class Dummy(object):
    pass
d = Dummy()
d.does_not_exist  # Fails with AttributeError

In this example, the attribute access fails (with an AttributeError) because the attribute does_not_exist doesn’t exist.

But using the __getattr__ magic method, we can intercept that inexistent attribute lookup and do something so it doesn’t fail:

class Dummy(object):
    def __getattr__(self, attr):
        return attr.upper()
d = Dummy()
d.does_not_exist # 'DOES_NOT_EXIST'
d.what_about_this_one  # 'WHAT_ABOUT_THIS_ONE'

But if the attribute does exist, __getattr__ won’t be invoked:

class Dummy(object):
    def __getattr__(self, attr):
        return attr.upper()
d = Dummy()
d.value = "Python"
print(d.value)  # "Python"

__getattribute__

__getattribute__ is similar to __getattr__, with the important difference that __getattribute__ will intercept EVERY attribute lookup, doesn’t matter if the attribute exists or not. Let me show you a simple example:

class Dummy(object):
    def __getattribute__(self, attr):
        return 'YOU SEE ME?'
d = Dummy()
d.value = "Python"
print(d.value)  # "YOU SEE ME?"

In that example, the d object already has an attribute value. But when we try to access it, we don’t get the original expected value (“Python”); we’re just getting whatever __getattribute__ returned. It means that we’ve virtually lost the value attribute; it has become “unreachable”.

If you ever need to use __getattribute__ to simulate something similar to __getattr__ you’ll have to do some more advanced Python handling:

class Dummy(object):
    def __getattribute__(self, attr):
        __dict__ = super(Dummy, self).__getattribute__('__dict__')
        if attr in __dict__:
            return super(Dummy, self).__getattribute__(attr)
        return attr.upper()

d = Dummy()
d.value = "Python"
print(d.value)  # "Python"
print(d.does_not_exist)  # "DOES_NOT_EXIST"

A more realistic example

It’s not common to just randomly catch every attribute lookup. We generally use __getattr__ with a clear objective in mind: whenever we want to provide some dynamic access to our objects. A good example could be extending the base Python tuple to add it some Scala flavor to it. In Scala, tuples are created really similarly to Python:

val a_tuple = ("z", 3, "Python", -1)

But they’re accessed in a different way:

println(a_tuple._1) // “z”
println(a_tuple._3) // “Python”

Each element in the tuple is accessed as an attribute, with the first element being the attribute _1, the second _2, and so on.

In that example, you can see how we’re catching missing attributes with __getattr__, but if that attribute is not in the form of _n (where n is an integer), we just raise the AttributeError manually.

We can easily extend our common Python tuple to match this behavior, the code is really simple:

class Tuple(tuple):
    def __getattr__(self, name):
        def _int(val):
            try:
                return int(val)
            except ValueError:
                return False

        if not name.startswith('_') or not _int(name[1:]):
            raise AttributeError("'tuple' object has no attribute '%s'" % name)
        index = _int(name[1:]) - 1
        return self[index]


t = Tuple(['z', 3, 'Python', -1])
print(t._1)  # 'z'
print(t._2)  # 3
print(t._3)  # 'Python'

t = Tuple(['z', 3, 'Python', -1])

assert t._1 == 'z'
assert t._2 == 3
assert t._3 == 'Python'
assert t._4 == -1

getattr()

getattr (object, name[, default])is one of Python’s built-in functions, its role is to get the properties of the object.

  • Object object
  • Name attribute name
  • Default The default value is returned when the property does not exist

Example:

class Foo:
    def __init__(self, x):
        self.x = x

f = Foo(10)
getattr(f, 'x')
f.x  # 10
getattr(f, 'y', 'bar')  # 'bar'

__getattr__

object. __getattr__(self, name)Is an object method that is called if the object’s properties are not found.

This method should return the property value or throw AttributeError.

Note that if the object property can be found through the normal mechanism, it will not be called.__getattr__method.

Example:

class Frob:     
    def __init__(self, bamf):         
        self.bamf = bamf     
    def __getattr__(self, name):       
        return 'Frob does not have `{}` attribute.'.format(str(name))

f = Frob("bamf")
f.bar # 'Frob does not have `bar` attribute.'
f.bamf # f'bamf'

__getattribute__

This method is called unconditionally when accessing the properties of an object. This method only works for new classes.
The new class is a class that integrates from object or type.

If the class is also defined at the same time__getattr__(), it will not be called__getattr__() unless__getattribute__() shows the call__getattr__()Or thrown AttributeError. The method should return the property value or throw AttributeError. To avoid infinite recursion in methods, you should always use the methods of the base class to get the properties:

object.__getattribute__(self, name).

grammar:object. __getattribute__(self, name)

Example:

class Frob(object):
    def __getattribute__(self, name):
        print (f"getting `{str(name)}`")
        return object.__getattribute__(self, name)

f = Frob()
f.bamf = 10
f.bamf # getting `bamf`10

__ get __

__get__()The method is one of the descriptor methods. Descriptors are used to transform access object properties into call descriptor methods.

Example:

class Descriptor(object):
    def __get__(self, obj, objtype):
        print(f"get value={self.val}")
        return self.val

    def __set__(self, obj, val):
        self.val = val
class Stu(object):
    age = Descriptor()

stu = Stu()
stu.age = 12

Conclusion

Magic Methods are a great mechanism to extend the basic features of Python classes and objects and provide more intuitive interfaces. You can provide dynamic attribute lookups with __getattr__ for those missing attributes that you want to intercept. But be careful with __getattribute__, because it might be tricky to implement correctly without losing attributes in the Python void.

2 Comments

  1. M. says:

    man, you have typos in the code cells! after class definition, there should be a new line!
    like here:
    “`
    class Dummy(object):
    pass
    d = Dummy()
    d.does_not_exist # Fails with AttributeError
    “`

Leave a Reply

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