Python descriptor (descriptor), pythondescriptor

Source: Internet
Author: User

Python descriptor (descriptor), pythondescriptor

1. What is a descriptor?

The python descriptor is an object attribute of "binding behavior". In the descriptor protocol, it can override the access to the attribute through methods. These methods include _ get _ (), _ set _ (), and _ delete __(). If any of these methods is defined in an object, this object is a descriptor.

The above is an official definition. It is purely intended for installation and use. Most people think that these definitions have an impulse to greet their ancestors!

It doesn't matter. After reading this article, you will understand what a descriptor is!

2. Before explaining the descriptor, let's take a look at the attribute __dict _ (each object has this attribute)

Function: it is a dictionary type that stores the attributes of this object. The key (key) is the attribute name, and the value (value) is the attribute value in the form of {attr_key: attr_value}

Access sequence of object attributes:

①. Instance attributes

②. Class attributes

③. Parent class attributes

④. _ Getattr _ () method

Please remember the above order!

1 class Test (object): 2 cls_val = 1 3 def _ init _ (self): 4 self. ins_val = 10 5 6 7 >>> t = Test () 8 >>> Test. _ dict _ 9 mappingproxy ({'_ module _': '_ main _', 'cls _ val': 1, '_ init _': <function Test. _ init _ at 0x0000000002E35D08>, '_ dict _': <attribute '_ dict _' of 'test' objects>, '_ weakref _': <attribute '_ weakref _' of 'test' objects>, '_ doc _': None }) 10 >>> t. _ dict _ 11 {'ins _ val': 10} 12 13 >>> type (x) = X14 True15 16 # change the attribute cls_val of instance t, this attribute is added but does not affect the cls_val17 >>> t. cls_val = 2018 >>> t. _ dict _ 19 {'ins _ val': 10, 'cls _ val': 20} 20 >>> Test. _ dict _ 21 mappingproxy ({'_ module _': '_ main _', 'cls _ val': 1, '_ init _': <function Test. _ init _ at 0x0000000002E35D08>, '_ dict _': <attribute '_ dict _' of 'test' objects>, '_ weakref _': <attribute '_ weakref _' of 'test' objects>, '_ doc _': None }) 22 23 # changed the cls_val attribute of the Test class. Because the cls_val attribute of instance t is added in advance, the cls_val value of the instance is not changed (the well water does not harm the river) 24 >>> Test. cls_val = 3025 >>> t. _ dict _ 26 {'ins _ val': 10, 'cls _ val': 20} 27 >>> Test. _ dict _ 28 mappingproxy ({'_ module _': '_ main _', 'cls _ val': 30, '_ init _': <function Test. _ init _ at 0x0000000002E35D08>, '_ dict _': <attribute '_ dict _' of 'test' objects>, '_ weakref _': <attribute '_ weakref _' of 'test' objects>, '_ doc _': None })

From the code above, we can see that the attributes of instance t do not contain cls_val, and cls_val belongs to the Test class.

3. Magic method: __get _ (), _ set _ (), _ delete __()

The method is prototype:

① _ Get _ (self, instance, owner)

② _ Set _ (self, instance, value)

③ _ Del _ (self, instance)

So what about self and instance owner? Don't worry, listen to me slowly!

First, let's look at a piece of code:

1 # code 1 2 3 class Desc (object): 4 5 def _ get _ (self, instance, owner): 6 print ("_ get __... ") 7 print (" self: \ t ", self) 8 print (" instance: \ t ", instance) 9 print (" owner: \ t ", owner) 10 print ('=' * 40, "\ n") 11 12 def _ set _ (self, instance, value ): 13 print ('_ set __... ') 14 print ("self: \ t", self) 15 print ("instance: \ t", instance) 16 print ("value: \ t", value) 17 print ('=' * 40, "\ n") 18 19 20 class TestDesc (object): 21 x = Desc () 22 23 # below is the test code 24 t = TestDesc () 25 t. x26 27 # The following is the output: 28 29 _ get __... 30 self: <__ main __. desc object at 0x0000000002B0B828> 31 instance: <__ main __. testDesc object at 0x0000000002B0BA20> 32 owner: <class '_ main __. testDesc '> 33 ======================================== ====

 

After TestDesc is instantiated, the _ get _ method of the Desc class is automatically called when the object t is called to access its attribute x. The output information shows that:

① Self: Desc instance object is actually the property of TestDesc x

② Instance: the instance Object of TestDesc is actually t

③ Owner: who owns these things, of course, is the class TestDesc. It is the highest ruler, and others are contained inside or generated by it.

At this point, I can uncover a small mystery. In fact, the Desc class is a descriptor (descriptor is a class). Why? Because the Desc class defines the method _ get __, _ set __.

So,A class can be called a descriptor as long as one or more methods _ get __, _ set __, and _ delete _ are defined internally.(^ _ ^, Simple)

 

Speaking of this, our task is far from complete, and there are still many questions?

Question 1: Why does the _ get _ () method of the descriptor be called directly during access to t. x?

A: t is an instance. When you access t. x, it is in the regular order,

First, access the Owner's _ getattribute _ () method (actually TestDesc. _ getattribute _ (): Access the instance property. If no, access the parent class TestDesc!

Second, judge that attribute x is a descriptor. In this case, it will make some changes and change TestDesc. convert x to TestDesc. _ dict _ ['X']. _ get _ (None, TestDesc) to access

Then, enter the _ get _ () method of the Desc class and perform the corresponding operations.

Question 2. From code 1 above, we can see that the descriptor object x is actually a class attribute of the TestDesc class. Can we convert it into an instance attribute?

A: I said you did not count. If you did, the interpreter has the final say. Let's see what the interpreter says.

1 # Code 2 2 3 class Desc (object): 4 def _ init _ (self, name): 5 self. name = name 6 7 def _ get _ (self, instance, owner): 8 print ("_ get __... ") 9 print ('name = ', self. name) 10 print ('=' * 40, "\ n") 11 12 class TestDesc (object): 13 x = Desc ('x ') 14 def _ init _ (self): 15 self. y = Desc ('y') 16 17 # The following test code is 18 t = TestDesc () 19 t. x20 t. y21 22 # The following is the output result: 23 _ get __... 24 name = x25 ======================================== ====

 

Sorry, why didn't I print t. y's information?

Because there is no access _ get _ () method, haha, why is there no access _ get _ () method? (There are many problems)

Because t. at Moment y, TestDesc (Owner)'s _ getattribute _ () method will be called first. y to TestDesc. _ dict _ ['y']. _ get _ (t, TestDesc). However, in fact, TestDesc does not have the property "y". y belongs to the instance object. Therefore, it can only be ignored.

Question 3. What if the descriptor object of the class attribute has the same name as the object of the Instance attribute descriptor?

A: Let the interpreter explain it.

1 # code 3 2 3 class Desc (object): 4 def _ init _ (self, name): 5 self. name = name 6 print ("_ init _ (): name =", self. name) 7 8 def _ get _ (self, instance, owner): 9 print ("_ get __()... ") 10 return self. name11 12 def _ set _ (self, instance, value): 13 self. value = value14 15 class TestDesc (object): 16 _ x = Desc ('x') 17 def _ init _ (self, x): 18 self. _ x = x19 20 21 # below is the test code 22 t = TestDesc (10) 23 t. _ x24 25 # input result 26 _ init _ (): name = x27 _ get __()...

 

No, according to the Convention, t. _ x calls the _ getattribute _ () method and finds the _ x attribute of instance t, why is the _ get _ () method of the descriptor still called?

This involves a query order problem: when the Python interpreter finds that the instance object dictionary has an attribute with the same name as the descriptor, the descriptor takes priority and overwrites the instance attribute.

Believe it? Let's look at the dictionary:

1 >>> t.__dict__2 {}3 4 >>> TestDesc.__dict__5 mappingproxy({'__module__': '__main__', '_x': <__main__.Desc object at 0x0000000002B0BA20>, '__init__': <function TestDesc.__init__ at 0x0000000002BC59D8>, '__dict__': <attribute '__dict__' of 'TestDesc' objects>, '__weakref__': <attribute '__weakref__' of 'TestDesc' objects>, '__doc__': None})

 

How are you? I am old and never lie!

Let's improve code 3 and try to delete the _ set _ () method. What will happen?

1 # code 4 2 3 class Desc (object): 4 def _ init _ (self, name): 5 self. name = name 6 print ("_ init _ (): name =", self. name) 7 8 def _ get _ (self, instance, owner): 9 print ("_ get __()... ") 10 return self. name11 12 class TestDesc (object): 13 _ x = Desc ('x') 14 def _ init _ (self, x): 15 self. _ x = x16 17 18 # below is the test code 19 t = TestDesc (10) 20 t. _ x21 22 # output: 23 _ init _ (): name = x

 

I just got something wrong. What's going on? How can I call the _ get _ () method?

In fact, it is still the fault of attribute Search priority. It just defines a _ get _ () method, which is a non-data Descriptor and has a lower priority than the strength attribute !!

Question 4: What is a data Descriptor and what is a non-data descriptor?

A: For a class, if only the _ get _ () method is defined, but the _ set _ () and _ delete _ () methods are not defined, it is considered as a non-data descriptor; otherwise, it becomes a data descriptor.

Question 5: Can I sum up the attribute query priority every day?

A: Okay. Please wait!

① _ Getattribute _ (), called unconditionally

② Data descriptor: triggered by ① (if the _ getattribute _ () method is reloaded manually, the descriptor may not be called)

③ Instance object dictionary (if the same name as the descriptor object, it will be overwritten)

Class 4 dictionary

⑤ Non-data Descriptor

6. Dictionary of the parent class

7__ getattr _ () method

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.