Descriptor is mainly used to define methods and attributes in Python, and its use is quite technical. Here we will start from the basics and compile a concise guide to the use of descriptor descriptors in Python
When defining an iterator, the description is an object that implements the iteration protocol, that is, an object that implements the __iter__ method. In the same way, the so-called descriptor is an object that implements the descriptor protocol, that is, the __get__, __set__, and __delete__ methods.
Looking at the definition alone, it is quite abstract. talk is cheap. Look at the code:
class WebFramework(object): def __init__(self, name='Flask'): self.name = name def __get__(self, instance, owner): return self.name def __set__(self, instance, value): self.name = value class PythonSite(object): webframework = WebFramework() In [1]: PythonSite.webframework Out[1]: 'Flask' In [2]: PythonSite.webframework = 'Tornado' In [3]: PythonSite.webframework Out[3]: 'Tornado'
defines a class WebFramework, which implements the descriptor protocols __get__ and __set__. The object (class is also an object, everything are objects) becomes a descriptor. Implementing both __get__ and __set__ is called a data descriptor. Those that only implement __get__ are non-descriptors. The difference between the two is the priority of the dictionary relative to the instance.
If there is an attribute with the same name as the descriptor in the instance dictionary, if the descriptor is a data descriptor, the data descriptor will be used first. If it is a non-data descriptor, the attributes in the dictionary will be used first.
Call of descriptor
For this type of magic, the calling method is often not used directly. For example, decorators need to be called with the @ symbol. Iterators are usually used during iteration, or using the next method call. The descriptor is relatively simple and will be called when object attributes are set.
In [15]: webframework = WebFramework() In [16]: webframework.__get__(webframework, WebFramework) Out[16]: 'Flask'
Application of descriptor
The role of descriptor is mainly in the definition of methods and attributes. Since we can re-describe the properties of a class, this magic can change some behaviors of the class. The simplest application is to use a decorator to write a cache of class attributes. The author of Flask wrote a werkzeug network tool library, which uses the characteristics of the descriptor to implement a cache.
class _Missing(object): def __repr__(self): return 'no value' def __reduce__(self): return '_missing' _missing = _Missing() class cached_property(object): def __init__(self, func, name=None, doc=None): self.__name__ = name or func.__name__ self.__module__ = func.__module__ self.__doc__ = doc or func.__doc__ self.func = func def __get__(self, obj, type=None): if obj is None: return self value = obj.__dict__.get(self.__name__, _missing) if value is _missing: value = self.func(obj) obj.__dict__[self.__name__] = value return value class Foo(object): @cached_property def foo(self): print 'first calculate' result = 'this is result' return result f = Foo() print f.foo # first calculate this is result print f.foo # this is result
The running results are visible. First calculate only caches the results after it is calculated during the first call. The advantage of this is that in network programming, when parsing the HTTP protocol, the HTTP header is usually parsed into a dictionary of python. When using the view function, this header may be accessed at one time, so this header is used as a descriptor. Caching can reduce redundant parsing.
Descriptors are widely used in python, and are usually used together with decorators. With great magic comes great responsibility. Descriptors can also be used to implement "pre-compilation" of SQL statements in ORM. Proper use of descriptors can make your Python code more elegant.
For more detailed explanations of the concise guide to using the descriptor descriptor in Python, please pay attention to the PHP Chinese website for related articles!