Heim >Backend-Entwicklung >Python-Tutorial >Erste Schritte mit Python-Deskriptoren
Ich habe schon lange nicht mehr über Flask-Code geschrieben. Wenn ich jedoch darüber nachdenke, werde ich dieses Mal nicht schreiben Ich (du bist so eine Schlampe, beiß mich, wenn du kannst)
Dieses Mal werde ich über eine sehr wichtige Sache in Python schreiben, nämlich den Deskriptor
Erste Einführung in Deskriptoren
Alte Regel: Reden ist billig. Schauen wir uns zuerst einen Code an
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'
Das muss jedem bekannt sein Nun, wer kennt es nicht? Aber wissen Sie, was der Implementierungsmechanismus von Python ist? Ah. . . Nur ein Scherz, schauen wir uns den folgenden Code an
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__)
Sehen wir uns das Schritt für Schritt an. Aber hier geben wir zunächst eine Schlussfolgerung: Deskriptoren sind eine besondere Art von Objekten, die __get__ implementieren. __set__, __delete__ diese drei speziellen Methoden.
Detaillierte Erläuterung des Deskriptors
Sprechen Sie über Immobilien
Oben haben wir den Implementierungscode für Immobilien angegeben. Lassen Sie uns nun ausführlich darüber sprechen
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'
Wenn Sie sich mit Dekoratoren nicht auskennen, möchten Sie vielleicht diesen Artikel lesen. Kurz gesagt, bevor wir den Code offiziell ausführen, teilt uns unser Interpreter mit, dass wir den Code scannen und Ersetzen Sie die Teile, an denen Dekorateure beteiligt sind. Das Gleiche gilt für Klassendekorateure. Oben löst dieser Code
@Property deffull_name(self): """ Return the full name """ return"%s %s"% (self.first_name, self.last_name)
einen solchen Prozess aus, nämlich full_name=Property(full_name). Nachdem wir das Objekt später instanziiert haben, rufen wir auf Person.full_name Ein solcher Prozess entspricht tatsächlich person.full_name.__get__(person) und löst dann __get__() aus Die in die Methode geschriebene Rückgabe self.fget(obj) ist der Ausführungscode in def full_name, den wir ursprünglich geschrieben haben.
Zu diesem Zeitpunkt können Genossen über getter (), setter () und deleter () nachdenken Der spezifische Betriebsmechanismus =. =Wenn Sie noch Fragen haben, können Sie diese gerne in den Kommentaren besprechen.
Über Deskriptoren
Erinnern Sie sich noch an die zuvor erwähnte Definition: Deskriptoren sind eine besondere Art von Objekten, die __get__, __set__, __delete__ implementieren. Diese drei speziellen Methoden. In der Beschreibung der offiziellen Python-Dokumentation gibt es dann diesen Absatz, um die Bedeutung von Deskriptoren widerzuspiegeln: „Sie sind der Mechanismus dahinter.“ Eigenschaften, Methoden, statische Methoden, Klassenmethoden und super() Sie werden verwendet in Python selbst, um die neuen Stilklassen zu implementieren, die in eingeführt wurden Version 2.2. „Kurz gesagt, es gibt zuerst den Deskriptor und dann den Himmel, und jede Sekunde gibt es Luft. . Nun, in Klassen neuen Stils basieren Attribute, Methodenaufrufe, statische Methoden, Klassenmethoden usw. alle auf der spezifischen Verwendung von Deskriptoren.
OK, Sie möchten vielleicht fragen, warum Deskriptoren so wichtig sind? Machen Sie sich keine Sorgen, schauen wir uns die
Verwendung von Deskriptoren
an. Sehen Sie sich zunächst den nächsten Codeabschnitt
classA(object) an: #Hinweis: In Python 3. x-Version, für die Verwendung der neuen Klasse ist keine explizite Angabe der Vererbung von der Objektklasse If erforderlich
Die Version von Python 2. Bitte denken Sie darüber nach, was passiert, wenn wir diese Methode aufrufen?
defa(self): pass if__name__=="__main__": a=A() a.a()
Wenn wir zunächst ein Attribut aufrufen, sei es ein Mitglied oder eine Methode, lösen wir in unserem Beispiel eine solche Methode aus, um das Attribut __getattribute__() aufzurufen Wenn in der Methode __getattribute__() das Attribut, das wir aufzurufen versuchen, unser Deskriptorprotokoll implementiert, findet ein solcher Aufrufprozess statt type(a).__dict__['a'].__get__(b,type(b)) . Okay, hier haben wir eine weitere Schlussfolgerung: „In einem solchen Aufrufprozess gibt es eine solche Prioritätsreihenfolge, wenn das Attribut, das wir aufrufen möchten, ein Datendeskriptor ist.“ , dann wird unabhängig davon, ob dieses Attribut im __dict__-Wörterbuch unserer Instanz vorhanden ist, zuerst die __get__-Methode in unserem Deskriptor aufgerufen, wenn das Attribut, das wir aufrufen möchten, kein Datenattribut ist Deskriptoren, dann geben wir dem Aufruf der vorhandenen Attribute in __dict__ in unserer Instanz Vorrang. Wenn sie nicht vorhanden sind, durchsuchen wir unsere Klasse und __dict__ in der übergeordneten Klasse gemäß den entsprechenden Prinzipien. Für Attribute, die in enthalten sind, wird die Methode __get__ aufgerufen, sobald das Attribut vorhanden ist. Wenn sie nicht vorhanden ist, wird __getattr__() aufgerufen Methode". Ist es etwas abstrakt zu verstehen? Es ist in Ordnung, wir werden bald darüber sprechen, aber hier müssen wir zuerst Datendeskriptoren und Nicht-Datendeskriptoren erklären Schauen wir uns ein anderes Beispiel an. Was sind Datendeskriptoren und Nichtdatendeskriptoren? Tatsächlich ist es sehr einfach, __get__ auch im Deskriptor zu implementieren. Deskriptoren mit dem __set__-Protokoll sind Datendeskriptoren. Wenn nur das __get__-Protokoll implementiert ist, handelt es sich um Nicht-Datendeskriptoren. . Okay, schauen wir uns jetzt ein Beispiel an:
好的,让我们仔细来看看这段代码,首先类描述符 @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)!