A Free Trial That Lets You Build Big!
Start building with 50+ products and up to 12 months usage for Elastic Compute Service
1. Suppose that in a program, we need to perform strict type checking on an object property. However, Python is a dynamic language and therefore does not support type checking, but this does not prevent us from implementing our own version of a more elementary type check. The traditional approach to object property type checking may be in the following way:
def __init__ (self, Name, age): If Isinstance (str, name): Self.name = name Else:raise TypeError ("must bes a string") if ISI Nstance (int.): Self.age = Age else:raise TypeError ("must is an int")
The above is one way to perform this type of check, but it becomes cumbersome when the number of parameters increases. In addition, before assigning a value, we can create a Type_check (type, val) function that is called in __init__, but how can we simply implement such a check when we want to set the property value elsewhere. One of the quick solutions I've come up with is getters and setters in Java, but it's not Python-style and it's a bit cumbersome.
2. Suppose that in a program, we want to create some properties that initialize immediately and then become read-only at run time. Some people can also think of using special methods in Python, but this implementation is still clumsy and cumbersome.
3. Finally, imagine a program in which we want to customize access to object properties in some way. For example, you need to record access to this property. Similarly, a solution can be thought of, even if the solution might be cumbersome and not reusable.
The above problems are all associated with attribute references. Below, we will try to customize the access method of the property.
For the issues listed above, descriptors provide elegant, concise, robust, and reusable solutions. In short, a descriptor is an object that represents the value of an attribute. This means that if an account object has an attribute "name", then the descriptor is another object that can be used to represent the property "name" holding the value. The special methods of "defining __get__," "__set__," or "__delete__" in descriptor protocols are objects that implement one or more of these methods. The signature of each method in these methods is as follows:
Python descr.get (self,obj,type=none)->value. Descr.__set__ (self, obj, value)--none descr.__delete__ (self, obj)--None
The objects that implement the __get__ method are non-data descriptors, meaning that they can only be read after initialization. The objects that implement both __get__ and __set__ are data descriptors, which means that the property is writable.
To better understand the descriptor, we give a descriptor-based workaround for the above problem. Using Python descriptors to implement type checking of object properties is a very simple task. The code for the adorner to implement this type check is as follows:
Class Typedproperty (object): def __init__ (self, name, type, default=none): Self.name = "_" + name Self.type = Type sel F.default = Default if default else type () def __get__ (self, instance, CLS): Return GetAttr (instance, Self.name, self. Default) def __set__ (self,instance,value): If not isinstance (value,self.type): Raise TypeError ("Must is a%s"% self.) Type) setattr (instance,self.name,value) def __delete__ (self,instance): Raise Attributeerror ("Can ' t delete Attribute ") class Foo (object): Name = Typedproperty (" name ", str) num = typedproperty (" num ", int,42) >> acct = Foo () ;> acct.name = "Obi" >> acct.num = 1234>> print acct.num1234>> print acct.nameobi# trying to assign a String to number fails>> Acct.num = ' 1234 ' Typeerror:must is a
In this example, we implement a descriptor, typedproperty, and the descriptor class performs a type check on any property of the class it represents. It is important to note that descriptors can only be legally defined at the class level, not at the instance level. For example, in the __init__ method in the example above.
When accessing any property of an instance of Class Foo, the descriptor invokes its __get__ method. It is important to note that the first parameter of the __get__ method is the source object to which the descriptor represents the property being referenced. When the property is assigned, the descriptor calls its __set__ method. To understand why it is possible to use descriptors to represent object properties, we need to understand how property reference resolution is performed in Python. For an object, the attribute resolution mechanism is in object.__getattribute__ (). The method converts b.x to type (b). __dict__[' x '].__get__ (b, type (b)). Then, the parsing mechanism uses the priority chain search attribute, in the priority chain, the data descriptor found in the class dictionary takes precedence over the instance variable, the instance variable takes precedence over the non-data descriptor, and if GetAttr () is provided, the priority chain assigns the lowest priority to GetAttr (). For a given object class, you can override the priority chain by customizing the __getattribute__ method.
After a deep understanding of the priority chain, it is easy to come up with an elegant solution to the second and third questions raised earlier. That is, using a descriptor to implement a read-only property becomes a simple case of implementing a data descriptor, that is, a descriptor without the __set__ method. Although not important in this example, the problem of defining access methods requires only adding the required functionality in the __get__ and __set__ methods.
Every time we want to use a descriptor, we have to define the descriptor class, which looks very cumbersome. The Python feature provides a concise way to add data descriptors to attributes. A property signature is as follows:
Property (Fget=none, Fset=none, Fdel=none, Doc=none) and property attribute
Fget, Fset, and Fdel are class getter, setter, and Deleter methods respectively. Let's illustrate how to create a property by using one of the following examples:
Class Accout (object): Def __init__ (self): Self._acct_num = None def get_acct_num (self): return self._acct_num def set_acct_num (self, value): Self._acct_num = value def del_acct_num (self): del self._acct_num acct_num = Property (Get_acct_num, Set_acct_num, Del_acct_num, "account Number property.")
If Acct is an instance of account, Acct.acct_num will call Getter,acct.acct_num = value will call Setter,del Acct_num.acct_num will call deleter.
In Python, property objects and functions can be implemented using descriptor protocols as described in the Descriptor Guide, as follows:
Class Property (object): ' Emulate Pyproperty_type () in objects/descrobject.c ' def __init__ (self, fget=none, fset= None, Fdel=none, doc=none): Self.fget = fget Self.fset = Fset Self.fdel = Fdel if Doc is None and fget are not None:doc = fget.__doc__ self.__doc__ = Doc def __get__ (self, obj, objtype=none): If obj was None:return self if Self.fget is None : Raise Attributeerror ("unreadable attribute") return Self.fget (obj) def __set__ (self, obj, value): If Self.fset is No Ne:raise attributeerror ("can ' t set attribute") Self.fset (obj, value) def __delete__ (self, obj): If Self.fdel is None: Raise Attributeerror ("can ' t delete attribute") Self.fdel (obj) def getter (self, fget): return type (self) (fget, Self.fset, Self.fdel, self.__doc__) def setter (self, fset): return type (self) (self.fget, Fset, Self.fdel, self.__ doc__) def deleter (self, Fdel): return type (self) (self.fget, Self.fset, Fdel, self.__doc__)
Python also provides an @ property adorner, which you can use to create read-only properties. A Property object has getter, setter, and deleter adorner methods that can be used to create a copy of the property through the corresponding accessor function of the decorated function. The following example best explains this:
Class C (object): Def __init__ (self): self._x = None @property # The X property. The decorator creates a read-only prop Erty def x (self): return self._x @x.setter # The X property setter makes the property writeable Def x (self, value): SE lf._x = value @x.deleter def x (self): del self._x
If we want to make the property read-only, we can remove the setter method.
In the Python language, descriptors are widely used. Python functions, class methods, and static methods are examples of non-data descriptors. The descriptor Guide gives a basic description of how the enumerated Python objects are implemented using descriptors.
Start building with 50+ products and up to 12 months usage for Elastic Compute Service