Compare the usage of __getattr__ and __getattribute__ to obtain attributes in Python

WBOY
Release: 2016-07-21 14:53:13
Original
1677 people have browsed it

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

Copy after login

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

Copy after login

Run result:

letian
China
Anhui
Copy after login

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

Copy after login

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

Copy after login

Run result:

None
China
China
Copy after login

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

Copy after login

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'

Copy after login

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

Copy after login

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.

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template