Explanation of class and object descriptors in Python, and explanation of python Descriptors

Source: Internet
Author: User
Tags string to number

Explanation of class and object descriptors in Python, and explanation of python Descriptors

Descriptors is an esoteric but important part of the Python language. They are widely used in the Python kernel, and mastering Descriptors will add an additional skill to the toolbox of Python programmers. To pave the way for the next discussion of descriptors, I will describe some scenarios that programmers may encounter during daily programming activities. Then I will explain what the descriptors are, and how they provide elegant solutions for these scenarios. In this summary, I will use the new style class to refer to the Python version.

1. In a program, we need to perform a strict type check on an object attribute. However, Python is a dynamic language, so it does not support type checks, but it does not prevent us from implementing our own version, and it is a relatively basic type check. The traditional method for checking object property types may adopt the following method:

def __init__(self, name, age): if isinstance(str, name): self.name = name else: raise TypeError("Must be a string") if isinstance(int, age): self.age = age else: raise TypeError("Must be an int")

The above is a method for executing this type of check, but it will become cumbersome when the number of parameters increases. In addition, before assigning values, we can create a type_check (type, val) function called in _ init _, but when we want to set the attribute value elsewhere, how can this check be implemented simply. One of the quick solutions I think of is getters and setters in Java, but this is not in line with the Python style and is troublesome.

2. Assume that in a program, we want to create attributes that are initialized immediately at runtime and then changed to read-only. Some people can think of using special methods in Python for implementation, but this implementation method is still clumsy and cumbersome.

3. Finally, imagine that in a program, we want to customize access to object attributes in some way. For example, you need to record access to such attributes. In the same way, you can think of a solution, even though this solution may be cumbersome and reusable.

All the above problems are associated with attribute references. Next, we will try to access the custom attributes.
Python Descriptor

The descriptor provides an elegant, concise, robust, and reusable solution to the problems listed above. In short, a descriptor is an object, which represents the value of an attribute. This means that if an account object has a property "name", the descriptor is another object that can be used to represent the value held by the property "name. In the descriptor protocol, special methods such as _ get _, _ set _, and _ delete _ are defined, a descriptor is an object that implements one or more 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

Objects implementing the _ get _ method are non-data descriptors, meaning they can only be read after initialization. The objects implementing both _ get _ and _ set _ are data descriptors, which means this attribute can be written.

To better understand the descriptor, we provide a descriptor-based solution to the above problems. Using Python descriptors to check the type of object properties is a very simple task. The code for the decorator to perform this type check is as follows:

class TypedProperty(object):  def __init__(self, name, type, default=None): self.name = "_" + name self.type = type self.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 be 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 be a <type 'int'>

In this example, we implement a descriptor TypedProperty, and this descriptor class will perform a type check on any attribute of the class it represents. Note that this is important, that is, the descriptor can only be legally defined at the class level, but not at the instance level. For example, in the _ init _ method in the above example.

The descriptor calls its _ get _ method when there are any attributes of the Foo instance of the category class. Note that the first parameter of the __get _ method is the referenced source object of the attribute represented by the descriptor. When an attribute is assigned, the descriptor calls its _ set _ method. To understand why descriptors can be used to represent object attributes, we need to understand the execution method of attribute reference parsing in Python. For objects, the property parsing mechanism is in object. _ getattribute. This method converts B. x to type (B). _ dict _ ['X']. _ get _ (B, type (B )). Then, the resolution mechanism uses the priority chain to search for attributes. In the priority chain, the data descriptor found in the class dictionary has a higher priority than the instance variable, and the instance variable has a higher priority than the non-data descriptor, if getattr () is provided, the priority chain will assign the lowest priority to getattr. For a given object class, you can use the custom _ getattribute _ method to override the priority chain.

After a deep understanding of the Priority chain, it is easy to come up with an elegant solution to the second and third problems mentioned above. That is, using the descriptor to implement a read-only attribute will become a simple situation of implementing the data descriptor, that is, the descriptor without the _ set _ method. Although this example is not important, you only need to add the required functions in the _ get _ and _ set _ methods to define access methods.
Class attributes

Every time we want to use a descriptor, We have to define the descriptor class, which looks very tedious. The Python feature provides a simple way to add data descriptors to attributes. An Attribute signature is as follows:
 

property(fget=None, fset=None, fdel=None, doc=None) -> property attribute

Fget, fset, and fdel are respectively the getter, setter, and deleter methods of the class. The following example shows how to create attributes:

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 an Account, acct. acct_num will call getter. acct. acct_num = value will call setter, del acct_num.acct_num will call deleter.

In Python, attribute objects and functions can be implemented using the descriptor protocol as described in the descriptor guide, as shown below:

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 is not None: doc = fget.__doc__ self.__doc__ = doc  def __get__(self, obj, objtype=None): if obj is 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 None: 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 the @ property modifier, which can be used to create read-only attributes. An Attribute object has the getter, setter, and deleter modifier methods. You can use them to create a copy of the property through the corresponding accessor function of the decoration 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 property def x(self): return self._x  @x.setter # the x property setter makes the property writeable def x(self, value): self._x = value  @x.deleter def x(self): del self._x

If we want to make the attribute read-only, we can remove the setter method.

Descriptors are widely used in Python. Python functions, class methods, and static methods are examples of non-data descriptors. The descriptor Guide provides a basic description of how the listed Python objects are implemented using descriptors.

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.