Heim  >  Artikel  >  Backend-Entwicklung  >  Erste Schritte mit Python-Deskriptoren

Erste Schritte mit Python-Deskriptoren

WBOY
WBOYOriginal
2016-12-05 13:27:231016Durchsuche

Ich habe schon lange nicht mehr über Flask-Code geschrieben. Wenn ich darüber nachdenke, werde ich dieses Mal jedoch 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. Zeigen Sie mir zuerst den Code.

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'

Jeder muss mit dieser Generation vertraut sein, nun ja, Eigentum, wer kennt es nicht, aber kennen Sie den Umsetzungsmechanismus von Eigentum? Was ist nicht klar? Wie wäre es, Python zu lernen? . . 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__)

Scheint es nicht kompliziert zu sein? Schauen wir es uns Schritt für Schritt an. Aber hier geben wir zunächst eine Schlussfolgerung: Deskriptoren sind ein spezielles Objekt, das drei spezielle Methoden implementiert: __get__, __set__ und __delete__.

Detaillierte Beschreibung des Deskriptors

Sprechen Sie über Immobilien

Oben haben wir den Property-Implementierungscode 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, sollten Sie zunächst diesen Artikel lesen. Kurz gesagt: Bevor wir den Code offiziell ausführen, scannt unser Interpreter die Teile, an denen Dekoratoren beteiligt sind. Das Gleiche gilt für Klassendekorateure. Oben dieser Code

@Property
deffull_name(self):
"""
 Return the full name
 """
return"%s %s"% (self.first_name, self.last_name)

löst einen solchen Prozess aus, nämlich full_name=Property(full_name). Nachdem wir das Objekt später instanziiert haben, rufen wir person.full_name auf. Dieser Prozess entspricht tatsächlich person.full_name.__get__(person) und löst dann die in der Methode __get__() geschriebene Rückgabe self.fget(obj) aus Das Original oben ist der Ausführungscode in def full_name, den wir geschrieben haben.

Zu diesem Zeitpunkt können Genossen über die spezifischen Betriebsmechanismen von getter (), setter () und deleter () = nachdenken. =Wenn Sie noch Fragen haben, können Sie diese gerne in den Kommentaren besprechen.

Über Deskriptoren

Erinnern Sie sich an die zuvor erwähnte Definition: Deskriptoren sind eine besondere Art von Objekten, die drei spezielle Methoden implementieren: __get__, __set__ und __delete__. In der Beschreibung der offiziellen Python-Dokumentation gibt es dann diesen Absatz, um die Bedeutung von Deskriptoren widerzuspiegeln: „Sie sind der Mechanismus hinter Eigenschaften, Methoden, statischen Methoden, Klassenmethoden und super (). Sie werden in Python überall verwendet.“ selbst, um die in Version 2.2 eingeführten neuen Stilklassen zu implementieren. „Kurz gesagt, es gibt zuerst den Deskriptor und dann den Himmel, und als nächstes kommt die 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? Keine Sorge, schauen wir weiter

Deskriptor verwenden

Schauen Sie sich bitte zunächst den nächsten Codeabschnitt an

classA(object):#Hinweis: In der Python 3.x-Version ist es nicht erforderlich, die Vererbung von der Objektklasse für die Verwendung der neuen Klasse explizit anzugeben. Wenn sie in Python 2.X (x>) enthalten ist. 2) Version, dann benötigen Sie

defa(self):
pass
if__name__=="__main__":
 a=A()
 a.a()

Jeder hat bemerkt, dass wir eine solche Aussage a.a() haben. Okay, denken Sie jetzt bitte darüber nach, was passiert, wenn wir diese Methode aufrufen?

OK? Hast du es schon herausgefunden? NEIN? Okay, machen wir weiter

Wenn wir ein Attribut aufrufen, sei es ein Mitglied oder eine Methode, lösen wir zunächst eine solche Methode aus, um das Attribut __getattribute__() in unserer __getattribute__()-Methode aufzurufen, wenn das Attribut wir versuchen Aufruf ist implementiert. Unser Deskriptorprotokoll generiert einen solchen Aufrufprozess type(a).__dict__['a'].__get__(b,type(b)). Okay, hier haben wir eine andere Schlussfolgerung: „In einem solchen Aufrufprozess gibt es eine solche Prioritätsreihenfolge. Wenn das Attribut, das wir aufrufen möchten, ein Datendeskriptor ist, dann unabhängig davon, ob dieses Attribut im __dict__ unserer Instanz vorhanden ist Im Wörterbuch wird zuerst die Methode __get__ in unserem Deskriptor aufgerufen. Wenn das Attribut, das wir aufrufen möchten, kein Datendeskriptor ist, geben wir dem in unserer Instanz vorhandenen Attribut Priorität wird befolgt. Suchen Sie nach den Attributen, die im __dict__ unserer Klasse und in der übergeordneten Klasse enthalten sind. Sobald das Attribut vorhanden ist, wird die Methode __get__ aufgerufen. Etwas abstrakt zum Verständnis? Es ist in Ordnung, wir kommen gleich dazu, aber hier müssen wir zuerst Datendeskriptoren und Nicht-Datendeskriptoren erklären und uns dann ein Beispiel ansehen. Was sind Datendeskriptoren und Nichtdatendeskriptoren? Tatsächlich ist es sehr einfach. Deskriptoren, die sowohl die Protokolle __get__ als auch __set__ im Deskriptor implementieren, sind Datendeskriptoren. Okay, schauen wir uns jetzt ein Beispiel an:

importmath
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
classCircle:
def__init__(self, radius):
 self.radius = radius
pass

 @lazyproperty
defarea(self):
 print("Com")
returnmath.pi * self.radius *2

deftest(self):
pass
if__name__=='__main__':
 c=Circle(4)
 print(c.area)

好的,让我们仔细来看看这段代码,首先类描述符 @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

如上面的例子所描述的一样,我们可以判断所传入的值是否有效等等。

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn