Detailed explanation of the attribute access process of Python objects

Just want to answer one question: What happened when the compiler wanted to read obj.field?

**Seemingly simple property access, the process is quite tortuous. There are a total of the following steps: **

  1. If obj itself (an instance) has this attribute, return. If not, execute step 2

  2. If the class of obj has this attribute, return. If not, go to step 3.

  3. If there is this attribute in the parent class of obj class, return. If not, continue to execute 3 until all parent classes have been visited. If it still does not, execute step 4.

  4. Execute the obj.getattr method.

It can be verified by the following code:

classA(object):
 a ='a'classB(A):
 b ='b'classC(B):
 class_field ='class field'
 def __getattr__(self, f):print('Method {}.__getattr__ has been called.'.format(
  self.__class__.__name__))return f
c =C()
print c.a
print c.b
print c.class_field
print c.c

Output:

a
b
classfield
Method C.__getattr__ has been called.
c

PS: The attribute in python is different from the property. When the property is used, the parsing priority of the property is the highest. For details, please refer to the blog: From attribute to property.

Supplementary knowledge: in-depth understanding of python objects and attributes

Class attributes and instance attributes

First, let’s take a look at how class attributes and class instance attributes are stored in python. Use the dir method to view object attributes

classTest(object):
 pass
 test =Test()
# View class attributes
 dir(Test)['__class__','__delattr__','__dict__','__doc__','__format__','__getattribute__','__hash__','__init__','__module__','__new__','__reduce__','__reduce_ex__','__repr__','__setattr__','__sizeof__','__str__','__subclasshook__','__weakref__']
# View instance properties
 dir(test)['__class__','__delattr__','__dict__','__doc__','__format__','__getattribute__','__hash__','__init__','__module__','__new__','__reduce__','__reduce_ex__','__repr__','__setattr__','__sizeof__','__str__','__subclasshook__','__weakref__']

We mainly look at an attribute dict, because dict saves the attributes of the object, see the following example

classSpring(object):...   season ="the spring of class"... 

# View the properties saved by the Spring class
 Spring.__dict__
dict_proxy({'__dict__':<attribute '__dict__'of'Spring' objects ,'season':'the spring of class','__module__':'__main__','__weakref__':<attribute '__weakref__'of'Spring' objects ,'__doc__': None})

# Access class attributes in two ways
 Spring.__dict__['season']'the spring of class'
 Spring.season
' the spring of class'

It is found that dict has a'season' key, which is the attribute of this class, and its value is the data of the class attribute.

Let’s take a look at its instance properties

 s =Spring()
# Instance attributes__dict__Is empty
 s.__dict__
{}
# Is actually the class attribute pointed to
 s.season
' the spring of class'

# Create instance attributes
 s.season ="the spring of instance"
# In this way, the instance properties are not empty. The instance attribute and class attribute created at this time have the same name and overwrite it
 s.__dict__
{' season':'the spring of instance'}
 s.__dict__['season']'the spring of instance'
 s.season
' the spring of instance'

# Class attributes are not affected by instance attributes
 Spring.__dict__['season']'the spring of class'
 Spring.__dict__
dict_proxy({'__dict__':<attribute '__dict__'of'Spring' objects ,'season':'the spring of class','__module__':'__main__','__weakref__':<attribute '__weakref__'of'Spring' objects ,'__doc__': None})

# If the instance attribute is deleted,Class attribute
 del s.season
 s.__dict__
{}
 s.season
' the spring of class'

# Custom instance attributes have no effect on class attributes
 s.lang ="python"
 s.__dict__
{' lang':'python'}
 s.__dict__['lang']'python'

# Modify class attributes
 Spring.flower ="peach"
 Spring.__dict__
dict_proxy({'__module__':'__main__','flower':'peach','season':'the spring of class','__dict__':<attribute '__dict__'of'Spring' objects ,'__weakref__':<attribute '__weakref__'of'Spring' objects ,'__doc__': None})
 Spring.__dict__['flower']'peach'
# In the instance__dict__No change
 s.__dict__
{' lang':'python'}
# Cannot find the flower attribute in the instance, call the class attribute
 s.flower
' peach'

Let’s take a look at the methods contained in the class and how dict changes

# Definition class
 classSpring(object):...   def tree(self, x):...     self.x = x
... return self.x
... 
# Method tree in__dict__inside
 Spring.__dict__
dict_proxy({'__dict__':<attribute '__dict__'of'Spring' objects ,'__weakref__':<attribute '__weakref__'of'Spring' objects ,'__module__':'__main__','tree':<function tree at 0xb748fdf4,'__doc__': None})
 Spring.__dict__['tree']<function tree at 0xb748fdf4 

# Create an instance, but__dict__There is no method in
 t =Spring()
 t.__dict__
{}

# Execution method
 t.tree("xiangzhangshu")'xiangzhangshu'
# Instance method(t.tree('xiangzhangshu'))The first parameter(self, but did not write it out)Binding instance t, through self.x to set the value, that is, give t.__dict__Add attribute value.
 t.__dict__
{' x':'xiangzhangshu'}
# If x is not assigned to the attribute of self, but directly return, the result has changed
 classSpring(object):...   def tree(self, x):...return x
 s =Spring()
 s.tree("liushu")'liushu'
 s.__dict__
{}

Need to understand a point of view in python, everything is an object, whether it is a class or an instance, it can be regarded as an object, conforming to object.attribute, will have its own attributes

Use slots to optimize memory usage

By default, python stores instance attributes in a dictionary named dict in each instance, and the dictionary consumes a lot of memory (dictionaries need to use the underlying hash table to improve access speed), through the slots class attribute, in the tuple Instance attributes are stored in, without a dictionary, thus saving a lot of memory

# Defined in the class__slots__Attribute means that the attributes of all instances in this class are here. If millions of instances are active at the same time, it can save a lot of memory
 classSpring(object):...   __slots__ =("tree","flower")... 
# Take a closer look at dir()The result, and__dict__Attribute? No more, no more. In other words__slots__Put__dict__Squeezed out, it entered the attributes of the class.
 dir(Spring)['__class__','__delattr__','__doc__','__format__','__getattribute__','__hash__','__init__','__module__','__new__','__reduce__','__reduce_ex__','__repr__','__setattr__','__sizeof__','__slots__','__str__','__subclasshook__','flower','tree']
 Spring.__slots__('tree','flower')
# Instantiate
 t =Spring()
 t.__slots__('tree','flower')

# Assign attribute values by class
 Spring.tree ="liushu"
# The tree attribute is read-only,Instance cannot be modified
 t.tree ="guangyulan"Traceback(most recent call last):
 File "<stdin ", line 1,in<module 
AttributeError:'Spring' object attribute 'tree' is read-only
 t.tree
' liushu'

# For attributes assigned with class attributes, they can only be used to modify
 Spring.tree ="guangyulan"
 t.tree
' guangyulan'

# For attributes that are not assigned with class attributes, they can be modified through examples
 t.flower ="haitanghua"
 t.flower
' haitanghua'
# The value of the instance attribute is not passed back to the class attribute, you can also understand it as a newly created instance attribute with the same name
 Spring.flower
< member 'flower'of'Spring' objects 
# If you assign a value to the class attribute
 Spring.flower ="ziteng"
 t.flower
' ziteng'

If used properly, slots can save memory significantly, pay attention to the problem as needed

After defining slots in the class, the instance cannot have other attributes other than the names listed in slots

Every subclass must define slots to be familiar, because the interpreter ignores inheriting slots attributes

If werkref is not added to slots, the instance cannot be the target of weak references

Magic methods for properties

Look at a few magic methods

__ setattr__(self,name,value): If you want to assign a value to name, call this method.
__ getattr__(self,name): If name is accessed, and it does not exist, this method is called.
__ getattribute__(self,name): It is automatically called when name is accessed (note: this can only be used for new-style classes), regardless of whether name exists or not, it will be called.
__ delattr__(self,name): If you want to delete name, this method is called.
 classA(object):...   def __getattr__(self, name):...     print "You use getattr"...   def __setattr__(self, name, value):...     print "You use setattr"...     self.__dict__[name]= value
# a.x, according to the example at the beginning of this section, an error is to be reported. However, due to the use of__getattr__(self, name)Method, when it is found that x does not exist in the object&#39;s__dict__When it is in, it is called__getattr__, The so-called &quot;intercepting members&quot;.
 a =A()
 a.x
You use getattr

# When assigning values to the properties of the object, call__setattr__(self, name, value)Method, this method has a sentence self.__dict__[name]=value, through this statement, the attributes and data are saved to the object__dict__in
 a.x =7
You use setattr

# test__getattribute__(self,name)classB(object):...   def __getattribute__(self, name):...     print "you are useing getattribute"...return object.__getattribute__(self, name)
# The returned content uses return object.__getattribute__(self, name)Without using return self.__dict__[name]. Because if you use this method, it is to visit self.__dict__, As long as you access this property, you must call`getattribute``, Which leads to infinite recursion

# Visit non-existent members, you can see that they have been__getattribute__Intercepted, although an error will still be reported in the end.
 b =B()
 b.y
you are useing getattribute
Traceback(most recent call last):
 File "<stdin ", line 1,in<module 
 File "<stdin ", line 4,in __getattribute__
AttributeError:'B' object has no attribute 'y'

Property function

porperty can be used as a decorator to mark methods as features

classVector(object):
 def __init__(self, x, y):
 # Use two leading underscores to mark the attribute as private
 self.__x =float(x)
 self.__y =float(y)
  
 # The pornerty decorator marks the reading method as a feature
 @ property
 def x(self):return self.__x
    
 @ property
 def y(self):return self.__y
    
vector =Vector(3,4)print(vector.x, vector.y)

Use property to encapsulate functions as properties

classRectangle(object):"""
 the width and length of Rectangle
  """
 def __init__(self):
 self.width =0
 self.length =0

 def setSize(self, size):
 self.width, self.length = size
 def getSize(self):return self.width, self.length

if __name__ =="__main__":
 r =Rectangle()
 r.width =3
 r.length =4
 print r.getSize()  # (3,4)
 r.setSize((30,40))
 print r.width  # 30
 print r.length  # 40

This code can run normally, but the method of calling properties can be improved, as follows:

classRectangle(object):"""
 the width and length of Rectangle
  """
 def __init__(self):
 self.width =0
 self.length =0

 def setSize(self, size):
 self.width, self.length = size
 def getSize(self):return self.width, self.length
 # Use the property method to encapsulate functions as attributes, which is more elegant
 size =property(getSize, setSize)if __name__ =="__main__":
 r =Rectangle()
 r.width =3
 r.length =4
 print r.size   # (30,40)
 r.size =30,40
 print r.width  # 30
 print r.length  # 40

Use magic methods to achieve:

classNewRectangle(object):
 def __init__(self):
 self.width =0
 self.length =0
  
 def __setattr__(self, name, value):if name =='size':
  self.width, self, length = value
 else:
  self.__dict__[name]= value
      
 def __getattr__(self, name):if name =='size':return self.width, self.length
 else:
  raise AttrubuteErrir
      
if __name__ =="__main__":
 r =Rectangle()
 r.width =3
 r.length =4
 print r.size   # (30,40)
 r.size =30,40
 print r.width  # 30
 print r.length  # 40

Attribute acquisition order

Finally, let's take a look at the familiar acquisition sequence: get its attributes through an instance, if there is a corresponding attribute in dict, it will return the result directly; if not, it will find it in the class attribute.

Consider the following example:

classA(object):
 author ="qiwsir"
 def __getattr__(self, name):if name !="author":return"from starter to master."if __name__ =="__main__":
 a =A()
 print a.author # qiwsir
 print a.lang # from starter to master.

When a = A(), no attributes are created for the instance, or the dict of the instance is empty. But if you want to check a.author, because there is no attribute of the instance, you can look for it in the attribute of the class and find that it does exist, so it returns its value "qiwsir". However, when I was looking for a.lang, not only was it not in the instance attribute, but also in the class attribute, so the getattr() method was called. In the above class, there is this method, what if there is no getattr() method? If this method is not defined, an AttributeError will be raised, which has been seen earlier.

The above detailed explanation of the attribute access process of the Python object is all the content shared by the editor. I hope to give you a reference.

Recommended Posts

Detailed explanation of the attribute access process of Python objects
Detailed explanation of the principle of Python super() method
Detailed explanation of the usage of Python decimal module
Detailed explanation of the principle of Python function parameter classification
Detailed explanation of the principle of Python timer thread pool
Detailed explanation of the implementation steps of Python interface development
Detailed explanation of common tools for Python process control
Detailed explanation of the remaining problem based on python (%)
Detailed explanation of python backtracking template
Detailed explanation of python sequence types
The operation of python access hdfs
Detailed explanation of Python IO port multiplexing
Python from attribute to property detailed explanation
Detailed explanation of -u parameter of python command
Detailed explanation of Python guessing algorithm problems
Detailed explanation of the use of pip in Python | summary of third-party library installation
Detailed explanation of python standard library OS module
Detailed explanation of data types based on Python
Detailed explanation of Python web page parser usage examples
Consolidate the foundation of Python (4)
Consolidate the foundation of Python(7)
Consolidate the foundation of Python(6)
Consolidate the foundation of Python(5)
Consolidate the foundation of Python (3)
The usage of wheel in python
Detailed implementation of Python plug-in mechanism
Python handles the 4 wheels of Chinese
Python simulation of the landlord deal
What is the use of Python
Detailed explanation of the installation and use of SSH in the Ubuntu environment
Detailed usage of dictionary in Python
Example operation of python access Alipay
The premise of Python string pooling
Secrets of the new features of Python 3.8
The father of Python joins Microsoft
The usage of tuples in python
End the method of running python
Can Python implement the structure of the stack?
Learn the basics of python interactive mode
What are the required parameters of python
Logistic regression at the bottom of python
The usage of Ajax in Python3 crawler
Python solves the Tower of Hanoi game
Solve the conflict of multiple versions of python
What is the scope of python variables
Python implements the sum of fractional sequences
Detailed analysis of Python garbage collection mechanism
Two days of learning the basics of Python
What is the id function of python
The essence of Python language: Itertools library
What are the advantages of python language
The specific method of python instantiation object
python3 realizes the function of mask drawing
What is the prospect of python development
Detailed usage of Python virtual environment venv
What is the function body of python
[Centos8] The bumpy process of installing docker
The specific method of python import library
Solve the conflict of multiple versions of python
What is the function of adb in python
Ubuntu20.04 install Python3 virtual environment tutorial detailed explanation