Cara menggunakan python __getattr__ classmethod

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:

  • 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'
    
    3 that serves as the object initializer (sometimes incorrectly referred to as constructor)
  • 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'
    
    4 that provides a “string representation” of your object
  • 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'
    
    5 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 (

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'
6). 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 
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'
7, we do 
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'
8.

There are many magic methods, but as we will focus on 

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'
9and 
class Dummy(object):
    def __getattr__(self, attr):
        return attr.upper()d = Dummy()
d.value = "Python"
print(d.value)  # "Python"
0 in this post.

__getattr__

Let’s start with 

class Dummy(object):
    def __getattr__(self, attr):
        return attr.upper()d = Dummy()
d.value = "Python"
print(d.value)  # "Python"
1. 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):
    passd = Dummy()
d.does_not_exist  # Fails with AttributeError

Cara menggunakan python __getattr__ classmethod

In this example, the attribute access fails (with an 

class Dummy(object):
    def __getattr__(self, attr):
        return attr.upper()d = Dummy()
d.value = "Python"
print(d.value)  # "Python"
2) because the attribute 
class Dummy(object):
    def __getattr__(self, attr):
        return attr.upper()d = Dummy()
d.value = "Python"
print(d.value)  # "Python"
3 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'

Cara menggunakan python __getattr__ classmethod

But if the attribute does exist, 

class Dummy(object):
    def __getattr__(self, attr):
        return attr.upper()d = Dummy()
d.value = "Python"
print(d.value)  # "Python"
1 won’t be invoked:

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

Cara menggunakan python __getattr__ classmethod

__getattribute__

class Dummy(object):
    def __getattr__(self, attr):
        return attr.upper()d = Dummy()
d.value = "Python"
print(d.value)  # "Python"
0 is similar to 
class Dummy(object):
    def __getattr__(self, attr):
        return attr.upper()d = Dummy()
d.value = "Python"
print(d.value)  # "Python"
1, with the important difference that 
class Dummy(object):
    def __getattr__(self, attr):
        return attr.upper()d = Dummy()
d.value = "Python"
print(d.value)  # "Python"
0 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?"

Cara menggunakan python __getattr__ classmethod

In that example, the 

class Dummy(object):
    def __getattr__(self, attr):
        return attr.upper()d = Dummy()
d.value = "Python"
print(d.value)  # "Python"
8 object already has an attribute 
class Dummy(object):
    def __getattr__(self, attr):
        return attr.upper()d = Dummy()
d.value = "Python"
print(d.value)  # "Python"
9. But when we try to access it, we don’t get the original expected value (“Python”); we’re just getting whatever 
class Dummy(object):
    def __getattr__(self, attr):
        return attr.upper()d = Dummy()
d.value = "Python"
print(d.value)  # "Python"
0 returned. It means that we’ve virtually lost the 
class Dummy(object):
    def __getattr__(self, attr):
        return attr.upper()d = Dummy()
d.value = "Python"
print(d.value)  # "Python"
9 attribute; it has become “unreachable”.

If you ever need to use 

class Dummy(object):
    def __getattr__(self, attr):
        return attr.upper()d = Dummy()
d.value = "Python"
print(d.value)  # "Python"
0 to simulate something similar to 
class Dummy(object):
    def __getattr__(self, attr):
        return attr.upper()d = Dummy()
d.value = "Python"
print(d.value)  # "Python"
1 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 

class Dummy(object):
    def __getattr__(self, attr):
        return attr.upper()d = Dummy()
d.value = "Python"
print(d.value)  # "Python"
1 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 

class Dummy(object):
    def __getattribute__(self, attr):
        return 'YOU SEE ME?'d = Dummy()
d.value = "Python"
print(d.value)  # "YOU SEE ME?"
5, the second 
class Dummy(object):
    def __getattribute__(self, attr):
        return 'YOU SEE ME?'d = Dummy()
d.value = "Python"
print(d.value)  # "YOU SEE ME?"
6, and so on.

In that example, you can see how we’re catching missing attributes with 

class Dummy(object):
    def __getattr__(self, attr):
        return attr.upper()d = Dummy()
d.value = "Python"
print(d.value)  # "Python"
1, but if that attribute is not in the form of 
class Dummy(object):
    def __getattribute__(self, attr):
        return 'YOU SEE ME?'d = Dummy()
d.value = "Python"
print(d.value)  # "YOU SEE ME?"
8 (where 
class Dummy(object):
    def __getattribute__(self, attr):
        return 'YOU SEE ME?'d = Dummy()
d.value = "Python"
print(d.value)  # "YOU SEE ME?"
9 is an integer), we just raise the 
class Dummy(object):
    def __getattr__(self, attr):
        return attr.upper()d = Dummy()
d.value = "Python"
print(d.value)  # "Python"
2 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()

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"
1is 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__

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"
2Is an object method that is called if the object’s properties are not found.

This method should return the property value or throw

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

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

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

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.bam # 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

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"
5), it will not be called
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"
6 unless
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"
7 shows the call
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"
6Or thrown
class Dummy(object):
    def __getattr__(self, attr):
        return attr.upper()d = Dummy()
d.value = "Python"
print(d.value)  # "Python"
2. The method should return the property value or throw
class Dummy(object):
    def __getattr__(self, attr):
        return attr.upper()d = Dummy()
d.value = "Python"
print(d.value)  # "Python"
2. To avoid infinite recursion in methods, you should always use the methods of the base class to get the properties:

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'
0

grammar:

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

Example:

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'
1

__ get __

val a_tuple = ("z", 3, "Python", -1)
2The method is one of the descriptor methods. Descriptors are used to transform access object properties into call descriptor methods.

Example:

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'
2

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 

class Dummy(object):
    def __getattr__(self, attr):
        return attr.upper()d = Dummy()
d.value = "Python"
print(d.value)  # "Python"
1 for those missing attributes that you want to intercept. But be careful with 
class Dummy(object):
    def __getattr__(self, attr):
        return attr.upper()d = Dummy()
d.value = "Python"
print(d.value)  # "Python"
0, because it might be tricky to implement correctly without losing attributes in the Python void.