403 Stimmen

Verstehen von __get__ und __set__ und Python-Deskriptoren

Ich bin Versuch um zu verstehen, was die Deskriptoren von Python sind und wofür sie nützlich sind. Ich verstehe, wie sie funktionieren, aber hier sind meine Zweifel. Betrachten Sie den folgenden Code:

class Celsius(object):
    def __init__(self, value=0.0):
        self.value = float(value)
    def __get__(self, instance, owner):
        return self.value
    def __set__(self, instance, value):
        self.value = float(value)

class Temperature(object):
    celsius = Celsius()
  1. Warum brauche ich die Deskriptorklasse?

  2. Was ist instance y owner hier? (in __get__ ). Was ist der Zweck dieser Parameter?

  3. Wie würde ich dieses Beispiel aufrufen/verwenden?

1voto

Yonks Somarl Punkte 11

Sie würden sehen https://docs.python.org/3/howto/descriptor.html#properties

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__)

1voto

yogender Punkte 317

Leicht verdaulich (mit Beispielen) Erläuterung für __get__ & __set__ & __call__ im Unterricht, was ist Owner, Instance ?

Einige Punkte zum Nachdenken, bevor Sie eintauchen:

  1. __get__ __set__ werden Deskriptoren der Klasse genannt, um ihre internen Attribute zu bearbeiten/speichern, nämlich: __name__ (Name der Klasse/Besitzerklasse), Variablen - __dict__ usw. Ich werde später erklären, was ein Eigentümer ist
  2. Deskriptoren werden in Design-Patterns häufiger verwendet, z. B. mit Dekoratoren (um Dinge zu abstrahieren). Man kann davon ausgehen, dass sie häufiger in der Software-Architektur verwendet werden, um Dinge weniger redundant und besser lesbar zu machen (scheint ironisch). Auf diese Weise werden die Prinzipien von SOLID und DRY eingehalten.
  3. Wenn Sie keine Software entwickeln, die den SOLID- und DRY-Prinzipien entsprechen soll, brauchen Sie sie wahrscheinlich nicht, aber es ist immer sinnvoll, sie zu verstehen.

1. Berücksichtigen Sie diesen Code:

class Method:
    def __init__(self, name):
        self.name = name
    def __call__(self, instance, arg1, arg2):
        print(f"{self.name}: {instance} called with {arg1} and {arg2}")

class MyClass:
    method = Method("Internal call")

instance = MyClass()

instance.method("first", "second")

# Prints:TypeError: __call__() missing 1 required positional argument: 'arg2'

Also, wenn instance.method("first", "second") genannt wird, __call__ Methode wird von der Klasse Method aufgerufen (call method macht ein Klassenobjekt einfach aufrufbar wie eine Funktion - wann immer eine Klasseninstanz aufgerufen wird __call__ wird instanziiert), und die folgenden Argumente werden zugewiesen: instance: "first", arg1: "second" und das letzte arg2 ausgelassen wird, wird ein Fehler ausgegeben: TypeError: __call__() missing 1 required positional argument: 'arg2'

2. Wie kann man das Problem lösen?

  • から __call__ nimmt instance als erstes Argument (Instanz, arg1, arg2), aber instance von was?

  • Instance ist die Instanz der Hauptklasse (MyClass), die die Deskriptorklasse (Method) aufruft. So, instance = MyClass() es el instance und wer ist also der owner ? die Klasse, die die Diskriptorklasse enthält - MyClass Es gibt jedoch keine Methode in unserer Deskriptorklasse (Method) sie als eine instance . Das ist also der Punkt, an dem wir brauchen __get__ Methode. Betrachten Sie noch einmal den folgenden Code:

    from types import MethodType class Method: def init(self, name): self.name = name def call(self, instance, arg1, arg2): print(f"{self.name}: {instance} called with {arg1} and {arg2}") def set(self, instance, value): self.value = value instance.dict["method"] = value def get(self, instance, owner): if instance is None: return self print (instance, owner) return MethodType(self, instance)

    class MyClass: method = Method("Internal call")

    instance = MyClass()

    instance.method("first", "second")

    Prints: Internal call: <main.MyClass object at 0x7fb7dd989690> called with first and second

vergessen einstellen. für den Moment laut den Unterlagen:

__get__ "Wird aufgerufen, um das Attribut der Eigentümerklasse (Klassenattribut-Zugriff) oder einer Instanz dieser Klasse (Instanzattribut-Zugriff) zu erhalten."

wenn Sie es tun: instance.method.__get__(instance)

Prints:<__main__.MyClass object at 0x7fb7dd9eab90> <class '__main__.MyClass'>

dies bedeutet Instanz: Objekt von MyClass das ist instance y Owner es MyClass selbst

3. __set__ Erläuterung:

__set__ wird verwendet, um einen Wert in der Klasse __dict__ Objekts (z. B. über eine Befehlszeile). Befehl zum Setzen des internen Werts für einstellen. ist: instance.descriptor = 'value' # wo der Deskriptor ist method in diesem Fall

  • ( instance.__dict__["method"] = value im Code aktualisieren Sie einfach die __dict__ Objekt des Deskriptors)

  • So auch hier: instance.method = 'value' jetzt zu prüfen, ob die value = 'value' wird in der Datei __set__ Methode können wir auf __dict__ Objekt des Deskriptors method . Machen: instance.method.__dict__ Drucke: {'_name': 'Internal call', 'value': 'value'}

  • Oder Sie können die __dict__ Wert mit vars(instance.method) Drucke: {'name': 'Internal call', 'value': 'value'} Ich hoffe, die Dinge sind jetzt klar:)

-1voto

Gregory Kuhn Punkte 1445

Ich habe (mit geringfügigen Änderungen, wie vorgeschlagen) den Code aus Andrew Cookes Antwort ausprobiert. (Ich benutze Python 2.7).

Der Code:

#!/usr/bin/env python
class Celsius:
    def __get__(self, instance, owner): return 9 * (instance.fahrenheit + 32) / 5.0
    def __set__(self, instance, value): instance.fahrenheit = 32 + 5 * value / 9.0

class Temperature:
    def __init__(self, initial_f): self.fahrenheit = initial_f
    celsius = Celsius()

if __name__ == "__main__":

    t = Temperature(212)
    print(t.celsius)
    t.celsius = 0
    print(t.fahrenheit)

Das Ergebnis:

C:\Users\gkuhn\Desktop>python test2.py
<__main__.Celsius instance at 0x02E95A80>
212

Mit Python vor 3, stellen Sie sicher, dass Sie Subklasse von Objekt, die den Deskriptor korrekt arbeiten, wie die erhalten. Magie funktioniert nicht für Klassen im alten Stil.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X