Home > Article > Backend Development > Compare the usage of __getattr__ and __getattribute__ to obtain attributes in Python
I believe that most of the time we don’t need to pay much attention to the details of getattribute and getattr (at least myself:)),
Under normal circumstances, when consuming our custom classes, we understand the structure of the class and will not deliberately deviate and cause some attribute access errors.
But as a curious, pursuing and temperamental Python baby, how could I not do a little research? Well, I actually read the source code of sinaweibopy, an open source project on github. The code is quite interesting and can be used as a practical example to see how to customize gettattr to make the code more dynamic and elegant:
# 例子在原来的基础上简化了一下,排除依赖和干扰,详细参见原项目 class UrlGenerator(object): def __init__(self, root_url): self.url = root_url def __getattr__(self, item): if item == 'get' or item == 'post': print self.url return UrlGenerator('{}/{}'.format(self.url, item)) url_gen = UrlGenerator('http://xxxx') url_gen.users.show.get >>> http://xxxx/users/show
Make full use of the feature that getattr will be called when the corresponding instance attribute is not found, and conveniently generate the corresponding URL through chain calls. When the http method is encountered in the source code, a
is returned.
Callable objects are more elegant, and chained operations are not only elegant but also well explain the meaning of the called interface (restful interface).
Example
1.__getattr__ example:
class Test(object): def __init__(self,name): self.name = name def __getattr__(self, value): if value == 'address': return 'China' if __name__=="__main__": test = Test('letian') print test.name print test.address test.address = 'Anhui' print test.address
Run result:
letian China Anhui
If an undefined method in a class is called, __getattr__ will also return a method, for example:
class Test(object): def __init__(self,name): self.name = name def __getattr__(self, value): return len if __name__=="__main__": test = Test('letian') print test.getlength('letian')
Run result:
6
2.__getattribute__ example:
class Test(object): def __init__(self,name): self.name = name def __getattribute__(self, value): if value == 'address': return 'China' if __name__=="__main__": test = Test('letian') print test.name print test.address test.address = 'Anhui' print test.address
Run result:
None China China
Think deeply
Since some elegant functions can be realized through the getattr custom method of the custom class, naturally we also need to have some understanding of it, including its similar custom method getattribute
1. Used as acquisition and interception of instance attributes
When accessing an instance attribute, getattribute will be called unconditionally. If your own getattr method is not implemented, an AttributeError will be thrown to prompt that the attribute cannot be found. If you have customized your own getattr method, the method will be in this way. Called when the property cannot be found, such as in the example above. So it is a good way to implement some functions by implementing a custom getattr method when the attribute cannot be found, because it will not be called every time like the getattribute method, which may affect some normal attribute access:
class Test(object): def __init__(self, p): self.p = p def __getattr__(self, item): return 'default' t = Test('p1') print t.p print t.p2 >>> p1 >>> default
2. Prevent infinite recursion when customizing getattribute
Because getattribute will always be called when accessing attributes, the custom getattribute method needs to return the corresponding attribute at the same time. Taking the value through self.__dict__ will continue to call getattribute downwards, causing a circular call:
class AboutAttr(object): def __init__(self, name): self.name = name def __getattribute__(self, item): try: return super(AboutAttr, self).__getattribute__(item) except KeyError: return 'default'
Here, the attributes of the formation are obtained by calling the bound super object. For new-style classes, it is actually the same as object.__getattribute__(self, item):
By default, customized classes will inherit the getattribute method from object, and attribute search is fully usable
The implementation of getattribute feels quite abstract. You only need to bind the corresponding instance object and the name of the attribute you want to find
3. When overriding getattribute and getattr at the same time, you need to imitate the original behavior and throw AttributeError in getattribute or manually call getattr
class AboutAttr(object): def __init__(self, name): self.name = name def __getattribute__(self, item): try: return super(AboutAttr, self).__getattribute__(item) except KeyError: return 'default' except AttributeError as ex: print ex def __getattr__(self, item): return 'default' at = AboutAttr('test') print at.name print at.not_exised >>>test >>>'AboutAttr' object has no attribute 'not_exised' >>>None
The getattr method in the above example will not be called at all, because the original AttributeError was handled by ourselves and was not thrown, and getattr was not called manually, so the result of accessing not_existed is None instead of default.