The descriptor in Python

Source: Internet
Author: User

The definition of descriptor descriptors in Python: Typically, we can assume that "a property of an object is bound ( __get__, __set__, __delete__Any one of these three methods ", then we call this property" descriptor "
class Foo(object):    def init(self, name, age):        self.name = name        self.age = agefoo = Foo("pizza", 18)
We cannot call Foo.name, foo.age These two attributes are descriptors, because none of them are bound to the above three methods. By default, the object's property access is achieved by using the three methods of Get, set, delete, which access the property's Dictionary __dict__. For example, a.x will first look for a.__dict__[' X '], and if not found, look for type (a). __dict__[' X '], and then keep looking up until metaclass (excluding Metaclass). As shown in the following code:
class Foo(object):    country = "China"    def __init__(self, name, age):        self.name = name        self.age = age        foo = Foo("pizza", 18)print(foo.__dict__)    # {‘name‘: ‘pizza‘, ‘age‘: 18}print(type(foo).__dict__)    # {‘__module__‘: ‘__main__‘, ‘country‘: ‘China‘, ‘__init__‘: <function Foo.__init__ at 0x103802488>, ‘__dict__‘: <attribute ‘__dict__‘ of ‘Foo‘ objects>, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Foo‘ objects>, ‘__doc__‘: None}
In the above code, if print (Foo.name) or print (foo.age) or find foo.__dict__If print (foo.country) looks for the type (foo) Foo.__dict__。 If a descriptor is encountered during a lookup, the Python interpreter replaces the lookup order with the method in the descriptor, in which case the __dict__ or descriptor of the object is first found, depending on the descriptor type, which we will demonstrate in the following subsections. Descriptor protocol
descr.__get__(self, obj, type=None) --> valuedescr.__set__(self, obj, value) --> Nonedescr.__delete__(self, obj) --> None
If any of the above three methods are defined, then we can assume that the object is a descriptor object that overrides the order in which the object's properties are looked up. As shown in the following code:
class Bar(object):    def __get__(self, instance, owner):        print("__get__")        def __set__(self, instance, value):        print("__set__")        def __delete__(self, instance, value):        print("__delete__")        class Foo(object):    bar = Bar()    foo = Foo()
In the above code, the bar attribute of Foo is considered to be a descriptor. Descriptor types are mentioned above, and descriptors are divided into Data descriptor and Non-data descriptor. If an object defines the __get__() and __set__() These two methods, then we think the object is a data descriptor. If you define only the __get__() method, that is Non-data descriptor, as shown in the following code: Data descriptor
class Bar(object):    def __get__(self, instance, owner):        print("get")    def __set__(self, instance, value):        print("__set__")    def __delete__(self, instance, value):        print("__delete__")class Foo(object):    bar = Bar()        foo = Foo()
In the above code, the Bar property of Foo is considered to be a descriptor, and it is the data descriptor. Non-data Descriptor
class Bar(object):    def __get__(self, instance, owner):        print("__get__")class Foo(object):    bar = Bar()foo = Foo()
In the above code, the Bar property of Foo is considered to be a descriptor, and it is non-data descriptor. The difference between Data and non-data descriptors is in the way that object properties are accessed. If the object's dictionary __dict__Has a property with the same name as data descriptor, data descriptor overwrites __dict__Lookup, as shown in the following code:
class Bar(object):    def __get__(self, instance, owner):        print("__get__")            def __set__(self, obj, value):        print("__set__")class Foo(object):    bar = Bar()    def __init__(self, name, age):        self.name = name        self.age = age        self.bar = "bar"foo = Foo("pizza", 18)foo.bar    # __get__
In the above code, the Bar property lookup of the Foo object executes the object's __get__Method. Because, Data descriptor will overwrite __dict__The lookup. If the object's dictionary __dict__Has a property with the same name as Non-data descriptor, then the object's __dict__The lookup overwrites Non-data descriptor, as shown in the following code:
class Bar(object):    def __get__(self, instance, owner):        print("__get__")        class Foo(object):    bar = Bar()    def __init__(self, name, age):        self.name = name        self.age = age        self.bar = "bar"foo = Foo("pizza", 18)foo.bar    # "bar"
In the above code, the Bar property lookup for Foo object will print "bar" because the object's __dict__The lookup overwrites the Non-data descriptor. The default property of Python in Python object-oriented design, there is a very important point of knowledge, called the property, it is implemented in a variety of ways, we demonstrate one of the following code:
class Foo(object):    def __init__(self, name):        self._name = name        @property    def name(self):        return self._name                foo = Foo("Pizza")print(foo._name)    # "Pizza"print(foo.name)    # "Pizza"
In the preceding code example, we use Foo._name to find the value of the property "Pizza", and we can also find the value of the property "Pizza" by Foo.name (because the property returns self._name)。 We do not make common function calls by defining a name method in the Foo class and then implementing the Access method through the default adorner property. In this process, there is a doubt, since you want to access the method of the object through the way of the property, and the return value is a property, then why not directly in the __init__A property is defined inside. Instead of simply returning the value of an existing property, for example, the property is dynamically getting a value and preserving the way the property is accessed, such as:
class Foo(object):    def __init__(self, name):        self._name = name            @property    def stock(self):        return 100 + 100                foo = Foo("Pizza")print(foo._name)    # "Pizza"print(foo.stock)    # 200
As we mentioned in the previous chapters, the implementation of Property,static Method,class method in Python relies on the mechanism of descriptor. So, next, we come from defining a property. Using descriptor custom property from the previous section, we can see that the method in the class is modified to a property, that is, the adorner @property is used. We know the adorner syntax sugar @decorator, equivalent to Func = Decorator (func), as shown in the following code:
class Foo(object):    def stock(self):        return 100 + 100    print(stock)    # <function Foo.stock at 0x101a57048>class Foo(object):    @property    def stock(self):        return 100 + 100    print(stock)    # <property object at 0x103811c78>
In the preceding code example, print (stock) results in a <property object at 0x103811c78>And <function Foo.stock at 0x101a57048>, even after adding @property to the stock method, the stock method becomes the object of the property, with the first print (stock) <function Foo.stock at 0x101a57048>Different. Next, our goal is to implement the custom property through descriptor. Before implementing a custom property, let's assume that there is a class, as shown in the following code:
class Foo(object):    def stock(self):        return 100 + 100foo = Foo()foo.stock
We know the following points:
    • Adorner syntax sugar @property equivalent to stock = property (stock);
    • A descriptor is an instantiated object of a class, such as Bar = Bar (), which is then defined in the bar class __get__, __set__, __delete__ ;
Our goal is to get a return value of 200 by means of a similar property access (Foo.stock) instead of a method call (Foo.stock ()). First, we use descriptors to implement simple property access, as shown in the following code:
class Stock(object):    def __get__(self, instance, owner):        print("__get__")        return 100 + 100                class Foo(object):    stock = Stock()        foo = Foo()foo.stock
At this point, the access to Foo.stock will first print __get__, and then show 200. So, what if we turn the stock into a method in the class? As shown in the following code:
class Stock(object):    def __get__(self, instance, owner):        print("__get__")        return 100 + 100class Foo(object):    def stock(self):        print("stock")
If the stock method can be changed into a descriptor, then we can access the descriptor by Foo.stock __get__method, and then gets its return value, both, 200. We know that turning a property into a descriptor is done directly by binding the property to the __get__method, such as: stock = stock (), but how to use the adorner syntax sugar? We know that the adorner syntax sugar @stock is equivalent to the stock = stock (stock), so we need to define one in the stock class __init__method, and define a formal parameter to receive the stock function that is passed in when the stock class is instantiated, as shown in the following code:
class Stock(object):    def __init__(self, stock):        self.stock = stock            def __get__(self, instance, owner):        print("__get__")        return 100 + 100                class Foo(object):    @Stock    # stock = Stock(stock)    def stock(self):        print("stock")
Through the above, the code, we will be in the Foo class of the stock method, successfully turned into a descriptor, next we can use the Foo class instantiation object to access the stock method, and using the Normal property invocation method, This is because the stock method in the Foo class is already a descriptor.
foo = Foo()foo.stock
The above code will print first __get__, and then show 200. In fact, the careful classmate will find that if the implementation of this way, we have achieved a custom property, but, with the official genuine property there is still a gap, the gap is that When accessing Foo.stock, the stock in the Foo class is not executed, and the properties in the original property are executed, that is, the value we need to get at the end is obtained directly from the property, as shown in the following code:
class Foo(object):    @property    def stock(self):        return 100 + 100                foo = Foo()foo.stock
In the above code example, we obtained the result 200 through foo.stock, which is calculated by the method of the stock in the Foo class. What if I want to use the same approach in my custom property? We know that in defining __get__method, it accepts three parameters, the first self represents descriptor, and below we print the second and third parameters, respectively, to see what they represent:
class Stock(object):    def __init__(self, stock):        self.stock = stock    def __get__(self, instance, owner):        print("__get__")        print("instance: ", instance)        print("owner:", owner)        return 100 + 100                class Foo(object):    @Stock    def stock(self):        print("stock")                foo = Foo()foo.stock
The above code executes the following results:
__get__instance:  <__main__.Foo object at 0x106c2b9b0>owner: <class ‘__main__.Foo‘>200
From the execution results of the above code can be seen, instance and owner of the two parameters, respectively, is passed to the Foo and Foo two objects, one is the Foo class instantiation object, and the Foo class itself, then whether we can use Foo or foo in __get__method, call the stock? The answer is no, because at this time the stock is already a descriptor, if the __get__method is called, then it goes into the dead loop and repeats the execution __get__Method. The original method of the stock in the Foo class was passed to the stock class in the @stock. __init__method is initialized, so at this point we can only invoke it in the way that is used in the following code example:
class Stock(object):    def __init__(self, stock):        self.stock = stock            def __get__(self, instance, owner):        return self.stock(instance)                class Foo(object):    @Stock    def stock(self):        return 100 + 100                foo = Foo()foo.stock
The above code executes the following results:
200
We can see from the results that the results are consistent with the previous implementation. Simply modify the code example to be more understandable, as follows:
class myproperty(object):    def __init__(self, stock):        self.stock = stock            def __get__(self, instance, owner):        return self.stock(instance)                class Foo(object):    @myproperty    def stock(self):        return 100 + 100                foo = Foo()foo.stock
At this point, we have implemented a custom property.

The descriptor in Python

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.