Python Property Descriptor (ii)

Source: Internet
Author: User

The way Python accesses properties is especially wrong, so when you read an attribute through an instance, you typically return a property defined in the instance, but if the instance has not defined the property, it gets the class property, and when you assign a value to an instance's property, the attribute is typically created in the instance without affecting the class itself. This unequal approach also has an effect on the descriptor class.

def cls_name (OBJ_OR_CLS): # Pass in an instance that returns the class name CLS = Type (OBJ_OR_CLS) if CLS is type:cls = obj_or_cls return cl S.__name__.split ('. ') [ -1]def display (obj): cls = Type (obj) If CLS is type: # If obj is a class, go to that branch return ' <class {}> '. Format (ob        j.__name__) elif cls in [Type (None), int]: # If obj is None or numeric, go to the branch return repr (obj) Else: # If obj is an instance  Return ' <{} object> '. Format (cls_name (obj)) def print_args (name, *args): Pseudo_args = ', '. Join (display (x) for X in args) print ('-}.__{}__ ({}) '. Format (Cls_name (args[0), name, Pseudo_args)) class overriding: # <1> "" "A.K.A. Data descriptor or enforced descriptor" "" Def __get__ (self, instance, owner): Print_args (' Get ', self, in Stance, owner) def __set__ (self, instance, value): Print_args (' Set ', self, instance, value) class Overridingnoget : # <2> "" An overriding descriptor without "__get__" "" "Def __set__ (self, instance, value): Print_a RGs(' Set ', self, instance, value) class Nonoverriding: # <3> "" "a.k.a. Non-data or Shadowable descriptor" "" Def _ _get__ (self, instance, owner): Print_args (' Get ', self, instance, owner) class Managed: # <4> over = Overri Ding () Over_no_get = Overridingnoget () Non_over = nonoverriding () def spam (self): # <5> print ('-&gt ; Managed.spam ({}) '. Format (display (self))

    

    1. There is a typical coverage descriptor for the __get__ and __set__ methods, in which each method calls the Print_args () function
    2. Override descriptor with no __get__ method
    3. There is no __set__ method, so this is a non-overlay descriptor
    4. Managed classes, using one instance of each descriptor class
    5. The spam method is put here for comparison, because the method is also a descriptor

Override Descriptor

The descriptor that implements the __set__ method is an overlay descriptor, although the descriptor is a class property, but if the __set__ method is implemented, the assignment to the instance property is overridden. The attribute is also an overlay descriptor, see Python Dynamic Properties and Attributes (ii), if no setpoint function is provided, the Fset method in the property class throws a Attributeerror exception, indicating that the attribute is read-only

Let's look at the behavior of the overlay descriptor:

>>> obj = Managed ()  # <1>>>> obj.over  # <2>-> overriding.__get__ (< overriding Object>, <managed object>, <class managed>) >>> managed.over  # <3>-> overriding.__get__ (<overriding object>, None, <class managed>) >>> obj.over = 8  # <4>- > overriding.__set__ (<overriding object>, <managed Object>, 8) >>> obj.over  # <5>- > overriding.__get__ (<overriding object>, <managed object>, <class managed>) >>> obj.__ Dict__["over"] = 9  # <6>>>> VARs (obj)  # <7>{' over ': 9}>>> obj.over  # <8 >-> overriding.__get__ (<overriding object>, <managed object>, <class managed>)

  

    1. Create a managed instance managed object
    2. Obj.over Trigger Descriptor __get__ Method, __get__ method The first parameter is a descriptor instance, the value of the second parameter is managed instance obj, and the third parameter is the managed class object
    3. Managed.over the __get__ method that triggers the descriptor, the value of the second argument (instance) is None, the first and third ibid.
    4. For Obj.over assignment, the __set__ method that triggers the descriptor, the value of the last parameter is 8
    5. __get__ method for reading obj.over that still triggers descriptors
    6. Skip descriptor, set value directly via Obj.__dict__ property
    7. Verify that the value is in the obj.__dict__ attribute, under the over key name
    8. However, even if the instance property named over, the Managed.over descriptor will still overwrite the read Obj.over operation

Override descriptor with no __get__ method

>>> obj.over_no_get  # <1><descriptorkinds. Overridingnoget object at 0x0000001742369780>>>> managed.over_no_get  # <2><descriptorkinds . Overridingnoget object at 0x0000001742369780>>>> obj.over_no_get = 8  # <3>-> Overridingnoget. __set__ (<overridingnoget object>, <managed Object>, 8) >>> obj.over_no_get  # <4>< Descriptorkinds. Overridingnoget object at 0x0000001742369780>>>> obj.__dict__[' over_no_get '] = 6  # <5>>> > Obj.over_no_get  # <6>6>>> obj.over_no_get = 7  # <7>-> overridingnoget.__set__ ( <overridingnoget object>, <managed Object>, 7) >>> obj.over_no_get  # <8>6

    

    1. The descriptor instance does not have a __get__ method, so Obj.over_no_get gets the descriptor instance from the class
    2. Read the Over_no_get property directly from the managed class, which is the descriptor instance
    3. Assigning a value to Obj.over_no_get hui triggers the __set__ method of the Descriptor class
    4. Because the __set__ method does not modify the property, this read obj.over_no_get gets the descriptor instance in the managed class still
    5. To set an instance property named Over_no_get through the __dict__ property of an instance
    6. The Over_no_get instance property now overrides the descriptor instance
    7. Assigning a value to a Obj.over_no_get instance property will still go through the __set__ method
    8. When read, the descriptor instance is overwritten whenever there is an instance property of the same name

Non-overriding descriptor: A descriptor that does not implement the __set__ method is called a non-blanket descriptor, and if an instance property of the same name is set, the descriptor is overwritten, causing the descriptor to be unable to process that property of that instance

>>> obj.non_over  # <1>-> nonoverriding.__get__ (<nonoverriding object>, <managed Object>, <class managed>) >>> obj.non_over = 6  # <2>>>> obj.non_over  # <3 >6>>> managed.non_over  # <4>-> nonoverriding.__get__ (<nonoverriding object>, None, <class managed>) >>> del obj.non_over  # <5>>>> obj.non_over  # <6>-> nonoverriding.__get__ (<nonoverriding object>, <managed object>, <class Managed>)

  

    1. Obj.non_over the __get__ method that triggers the descriptor, the value of the second parameter is obj
    2. Managed.non_over is a non-covered descriptor, so there is no __set__ method for intervening assignment operations
    3. Obj has an instance attribute named Non_over that obscures the descriptor attribute of the managed class with the same name.
    4. The Managed.non_over descriptor still exists and the access is intercepted through the class
    5. Delete Non_over Instance Properties
    6. The __get__ method that triggers the descriptor in the class when reading Obj.non_over

Override descriptor in class: Assigning a value to a class property overrides the descriptor, regardless of whether the descriptor is a covered type

  

>>> obj = Managed ()  # <1>>>> managed.over = 1  # <2>>>> Managed.over_no_ get = 2>>> Managed.non_over = 3>>> obj.over, obj.over_no_get, Obj.non_over  # <3> (1, 2, 3)

  

    1. Create a new Managed instance
    2. Overriding descriptor properties in a class
    3. Access descriptor properties via instance, new value override descriptor

A method is a descriptor: a function defined in a class is a binding method, and a user-defined function has a __get__ method, so it is equivalent to a descriptor when attached to a class, and the method is a non-blanket descriptor

>>> obj = Managed () >>> obj.spam  # <1><bound method Managed.spam of <descriptorkinds. Managed object at 0x00000017423284a8>>>>> managed.spam  # <2><function Managed.spam at 0x0000001742322ae8>>>> obj.spam = 7  # <3>>>> OBJ.SPAM7

  

    1. Obj.spam gets the Binding method object
    2. Managed.spam Gets the function
    3. If you assign a value to Obj.spam, the class attribute is obscured, resulting in the inability to access the spam method through the obj instance

The function does not have a __set__ method, so a non-covered descriptor, from the above example, Obj.spam and Managed.spam get different objects. As with descriptors, when accessed through a managed class, the function __get__ method returns its own reference, but when accessed through an instance, the __get__ method of the function returns the bound method object: A callable object that wraps the function, And binds a managed instance (such as obj) to the first argument of the function (that is, self), which is consistent with the behavior of the Functools.partial function

To understand this mechanism, let's look at one of the following examples:

Import Collectionsclass Text (collections. userstring):    def __repr__ (self):        return ' Text ({!r}) '. Format (self.data)    def reverse (self):        return SELF[::-1]

  

Test the Text class:

>>> Word = Text ("forward") >>> Word # <1>text (' forward ') >>> Word.reverse () # <2>t Ext (' drawrof ') >>> text.reverse (Text (' Backward ')) # <3>text (' Drawkcab ') >>> type ( Text.reverse), type (word.reverse) # <4> (<class ' function ', <class ' method ' >) >>> list (map ( Text.reverse, [' Repaid ', (ten, +, +), text (' stressed ')]) # <5>[' diaper ', (+, Ten), text (' Desserts ')]>>& Gt Func = text.reverse.__get__ (word) # <6>>>> func () # <7>text (' drawrof ') >>> text.reverse._ _get__ (None, Text) # <8><function text.reverse at 0x000000266209e598>>>> text.reverse< function Text.reverse at 0x000000266209e598>>>> Word.reverse # <9><bound method Text.reverse of Tex T (' forward ') >>>> text.reverse.__get__ (Word) <bound method Text.reverse of Text (' forward ') >>> > word.reverse.__self__ # <10>text (' Forward ') >>&Gt Word.reverse.__func__ is Text.reverse # <11>true

      

    1. The Repr method of the text instance returns a string similar to the text construction method call that can be used to create the same instance
    2. Reverse method returns a word that is spelled backwards
    3. Calling a method on a class and passing in an instance is equivalent to a function calling an instance
    4. There are different types of fetching methods from classes and getting methods from instances, one is function, one is method
    5. Text.reverse is equivalent to a function and can even handle objects other than the text instance
    6. Functions are non-blanket descriptors. When you call the __get__ method on a function, you pass in the instance, and you get the method that is bound to that instance.
    7. The Func object we get from the sixth step is the same as invoking a function on the instance
    8. When calling the __get__ method of a function, if the value of the instance parameter is none, then the function itself is obtained
    9. The word.reverse expression actually calls text.reverse.__get__ (word) and returns the corresponding binding method
    10. The binding method object has a __self__ property whose value is an instance reference that calls this method
    11. The __func__ property of the binding method is a reference to the original function attached to the managed class

The binding method object also has a __call__ method that handles the actual calling procedure, which invokes the original function referenced by the __func__ property, sets the first argument of the function to the __self__ property of the binding method, which is the implicit binding of the formal parameter self

Descriptor Usage Recommendations:

    • Use attributes to keep it simple: the built-in property class is actually an overlay descriptor, and both the __set__ method and the __get__ method are implemented, even if the set value method is not defined. The __set__ method of the attribute throws the Attributeerror:can ' t set attribute exception by default, so the simplest way to create a read-only property is to use the attribute
    • A read-only descriptor must have a __set__ method: If you implement a read-only property using a descriptor class, both __get__ and __set__ must be defined, otherwise the property with the same name in the instance will obscure the descriptor, and the __set__ method of the read-only property only needs to throw the Attributeerror exception , and provide the appropriate error message
    • The descriptor used for validation can have only the __set__ method: for the validation-only descriptor, the __set__ method should check the value obtained by the value parameter, and if valid, use the name of the descriptor instance as the key and set it directly in the instance's __dict__ property. This way, it is very fast to read the same name property from the instance because it is not processed by the __get__ method
    • Only descriptors with the __get__ method can be efficiently cached: If you write only the __get__ method, the non-overwrite descriptor is created. This descriptor can be used to perform some resource-intensive calculations, and then set the same name property for the instance to cache the results. An instance property of the same name obscures the descriptor, so subsequent accesses will fetch the value directly from the __dict__ property of the instance without triggering the __get__ method of the descriptor
    • Non-special methods can be obscured by instance properties: Because functions and methods implement only the __get__ method, they do not handle assignment operations for instance properties of the same name. Therefore, after a simple assignment like My_obj.the_method = 7, subsequent access to The_method through the instance is a number 7--but does not affect the class or other instance. However, special methods are not affected by this problem. The interpreter will only look for special methods in the class, that is, repr (x) is actually performing x.__class__.__repr__ (x), so the __repr__ property of X has no effect on the repr (x) method call. For the same reason, the _getattr__ property of an instance does not break the General property access rule

Python Property Descriptor (ii)

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.