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: **
If obj itself (an instance) has this attribute, return. If not, execute step 2
If the class of obj has this attribute, return. If not, go to step 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.
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's__dict__When it is in, it is called__getattr__, The so-called "intercepting members".
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