Home >Backend Development >Python Tutorial >Getting Started with Python Descriptors
I haven’t written about Flask code for a long time. I feel really ashamed when I think about it. However, I still won’t write about Flask this time. If you don’t accept it, come and beat me up (I’m such a bitch, bite me if you can)
I’ll do it this time Let’s write about a very important thing in Python, that is, Descriptor.
First introduction to descriptors
The old rule is, Talk is cheap, Show me the code. Let’s take a look at a piece of code first
classPerson(object): """""" #---------------------------------------------------------------------- def__init__(self, first_name, last_name): """Constructor""" self.first_name = first_name self.last_name = last_name #---------------------------------------------------------------------- @property deffull_name(self): """ Return the full name """ return"%s %s"% (self.first_name, self.last_name) if__name__=="__main__": person = Person("Mike","Driscoll") print(person.full_name) # 'Mike Driscoll' print(person.first_name) # 'Mike'
Everyone must be familiar with it. Well, who doesn’t know about property? But do you know the implementation mechanism of property? What’s unclear about it? Why don’t you learn Python? ah. . . Just kidding, let’s look at the following piece of code
classProperty(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 ifdocisNoneandfgetisnotNone: doc = fget.__doc__ self.__doc__ = doc def__get__(self, obj, objtype=None): ifobjisNone: returnself ifself.fgetisNone: raiseAttributeError("unreadable attribute") returnself.fget(obj) def__set__(self, obj, value): ifself.fsetisNone: raiseAttributeError("can't set attribute") self.fset(obj, value) def__delete__(self, obj): ifself.fdelisNone: raiseAttributeError("can't delete attribute") self.fdel(obj) defgetter(self, fget): returntype(self)(fget, self.fset, self.fdel, self.__doc__) defsetter(self, fset): returntype(self)(self.fget, fset, self.fdel, self.__doc__) defdeleter(self, fdel): returntype(self)(self.fget, self.fset, fdel, self.__doc__)
Does it look complicated? It’s okay, let’s take a look at it step by step. But here we first give a conclusion: Descriptors are a special kind of object, which implements __get__, __set__, __delete__ these three special methods.
Detailed explanation of descriptors
Let’s talk about Property
In the above, we gave the Property implementation code, now let us talk about this in detail
classPerson(object): """""" #---------------------------------------------------------------------- def__init__(self, first_name, last_name): """Constructor""" self.first_name = first_name self.last_name = last_name #---------------------------------------------------------------------- @property deffull_name(self): """ Return the full name """ return"%s %s"% (self.first_name, self.last_name) if__name__=="__main__": person = Person("Mike","Driscoll") print(person.full_name) # 'Mike Driscoll' print(person.first_name) # 'Mike'
First of all, if you don’t know about decorators, you may Go read this article. In short, before we officially run the code, our interpreter will scan our code and replace the parts involving decorators. The same goes for class decorators. In the above, this code
@Property deffull_name(self): """ Return the full name """ return"%s %s"% (self.first_name, self.last_name)
will trigger such a process, that is, full_name=Property(full_name) . Then after we instantiate the object later we call Person.full_name Such a process is actually equivalent to person.full_name.__get__(person) and then triggers __get__() The return self.fget(obj) written in the method is the execution code in def full_name that we originally wrote.
At this time, comrades can think about getter(), setter(), and deleter() The specific operating mechanism =. =If you still have questions, please feel free to discuss them in the comments.
About descriptors
Do you still remember the definition we mentioned before: Descriptors are a special kind of objects that implement __get__ , __set__ , __delete__ These three special methods. Then in the description of the official Python documentation, in order to reflect the importance of descriptors, there is this paragraph: "They are the mechanism behind properties, methods, static methods, class methods, and super(). They are used throughout Python itself to implement the new style classes introduced in version 2.2. ” In short, there is the descriptor first and then there is the sky, and every second there is air. . Well, in new-style classes, attributes, method calls, static methods, class methods, etc. are all based on the specific use of descriptors.
OK, you may want to ask, why are descriptors so important? Don't worry, let's continue to look at
Using descriptors
First, please look at the next piece of code
classA(object): #Note: In Python 3.x version, there is no need to explicitly specify the object from the use of new class class inherits, if in
In the version of Python 2. what happened?
defa(self): pass if__name__=="__main__": a=A() a.a()
好的,让我们仔细来看看这段代码,首先类描述符 @lazyproperty 的替换过程,前面已经说了,我们不在重复。接着,在我们第一次调用 c.area 的时候,我们首先查询实例 c 的 __dict__ 中是否存在着 area 描述符,然后发现在 c 中既不存在描述符,也不存在这样一个属性,接着我们向上查询 Circle 中的 __dict__ ,然后查找到名为 area 的属性,同时这是一个 non data descriptors ,由于我们的实例字典内并不存在 area 属性,那么我们便调用类字典中的 area 的 __get__ 方法,并在 __get__ 方法中通过调用 setattr 方法为实例字典注册属性 area 。紧接着,我们在后续调用 c.area 的时候,我们能在实例字典中找到 area 属性的存在,且类字典中的 area 是一个 non data descriptors ,于是我们不会触发代码里所实现的 __get__ 方法,而是直接从实例的字典中直接获取属性值。
描述符的使用
描述符的使用面很广,不过其主要的目的在于让我们的调用过程变得可控。因此我们在一些需要对我们调用过程实行精细控制的时候,使用描述符,比如我们之前提到的这个例子
classlazyproperty: def__init__(self, func): self.func = func def__get__(self, instance, owner): ifinstanceisNone: returnself else: value = self.func(instance) setattr(instance, self.func.__name__, value) returnvalue def__set__(self, instance, value=0): pass importmath classCircle: def__init__(self, radius): self.radius = radius pass @lazyproperty defarea(self, value=0): print("Com") ifvalue ==0andself.radius ==0: raiseTypeError("Something went wring") returnmath.pi * value *2ifvalue !=0elsemath.pi * self.radius *2 deftest(self): pass
利用描述符的特性实现懒加载,再比如,我们可以控制属性赋值的值
classProperty(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 ifdocisNoneandfgetisnotNone: doc = fget.__doc__ self.__doc__ = doc def__get__(self, obj, objtype=None): ifobjisNone: returnself ifself.fgetisNone: raiseAttributeError("unreadable attribute") returnself.fget(obj) def__set__(self, obj, value=None): ifvalueisNone: raiseTypeError("You can`t to set value as None") ifself.fsetisNone: raiseAttributeError("can't set attribute") self.fset(obj, value) def__delete__(self, obj): ifself.fdelisNone: raiseAttributeError("can't delete attribute") self.fdel(obj) defgetter(self, fget): returntype(self)(fget, self.fset, self.fdel, self.__doc__) defsetter(self, fset): returntype(self)(self.fget, fset, self.fdel, self.__doc__) defdeleter(self, fdel): returntype(self)(self.fget, self.fset, fdel, self.__doc__) classtest(): def__init__(self, value): self.value = value @Property defValue(self): returnself.value @Value.setter deftest(self, x): self.value = x
如上面的例子所描述的一样,我们可以判断所传入的值是否有效等等。
以上就是Python 描述符(Descriptor)入门,更多相关文章请关注PHP中文网(www.php.cn)!