1331 Stimmen

Wie funktioniert der @property-Dekorator in Python?

Ich würde gerne verstehen, wie die eingebaute Funktion property funktioniert. Was mich verwirrt, ist dass property auch als Dekorator verwendet werden kann, aber nur Argumente akzeptiert, wenn es als eingebaute Funktion verwendet wird und nicht wenn es als Dekorator verwendet wird.

Dieses Beispiel stammt aus der Dokumentation:

class C:
    def __init__(self):
        self._x = None

    def getx(self):
        return self._x
    def setx(self, value):
        self._x = value
    def delx(self):
        del self._x
    x = property(getx, setx, delx, "Ich bin die 'x' Eigenschaft.")

Die Argumente von property sind getx, setx, delx und eine Dokumentationszeichenkette.

In dem folgenden Code wird property als Dekorator verwendet. Das Objekt davon ist die Funktion x, aber im obigen Code gibt es keinen Platz für eine Objektfunktion in den Argumenten.

class C:
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """Ich bin die 'x' Eigenschaft."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

Wie werden die Dekoratoren x.setter und x.deleter in diesem Fall erstellt?

67voto

Bimo Punkte 5077

Das Folgende:

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """Ich bin die 'x'-Eigenschaft."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

Ist dasselbe wie:

class C(object):
    def __init__(self):
        self._x = None

    def _x_get(self):
        return self._x

    def _x_set(self, value):
        self._x = value

    def _x_del(self):
        del self._x

    x = property(_x_get, _x_set, _x_del, 
                    "Ich bin die 'x'-Eigenschaft.")

Ist dasselbe wie:

class C(object):
    def __init__(self):
        self._x = None

    def _x_get(self):
        return self._x

    def _x_set(self, value):
        self._x = value

    def _x_del(self):
        del self._x

    x = property(_x_get, doc="Ich bin die 'x'-Eigenschaft.")
    x = x.setter(_x_set)
    x = x.deleter(_x_del)

Ist dasselbe wie:

class C(object):
    def __init__(self):
        self._x = None

    def _x_get(self):
        return self._x
    x = property(_x_get, doc="Ich bin die 'x'-Eigenschaft.")

    def _x_set(self, value):
        self._x = value
    x = x.setter(_x_set)

    def _x_del(self):
        del self._x
    x = x.deleter(_x_del)

Was dasselbe ist wie:

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """Ich bin die 'x'-Eigenschaft."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

46voto

Divyanshu Rawat Punkte 3489

Beginnen wir mit Python-Dekoratoren.

Ein Python-Dekorator ist eine Funktion, die dazu beiträgt, zusätzliche Funktionalitäten zu einer bereits definierten Funktion hinzuzufügen.

In Python ist alles ein Objekt. Funktionen in Python sind Objekte der ersten Klasse, was bedeutet, dass sie von einer Variablen verwiesen, in Listen eingefügt, als Argumente an eine andere Funktion übergeben usw. werden können.

Betrachten Sie den folgenden Code-Ausschnitt.

def dekorator_func(fun):
    def wrapper_func():
        print("Wrapper-Funktion gestartet")
        fun()
        print("Angegebene Funktion dekoriert")
        # Die Wrapper-Funktion fügt der übergebenen Funktion etwas hinzu und der Dekorator
        # gibt die Wrapper-Funktion zurück
    return wrapper_func

def sag_tschuess():
    print("Tschüss!!")

sag_tschuess = dekorator_func(sag_tschuess)
sag_tschuess()

# Ausgabe:
#  Wrapper-Funktion gestartet
#  Tschüss!!
#  Angegebene Funktion dekoriert

Hier können wir sagen, dass die Dekoratorfunktion unsere sag_tschuess-Funktion geändert und einige zusätzliche Codezeilen hinzugefügt hat.

Python-Syntax für Dekorator

def dekorator_func(fun):
    def wrapper_func():
        print("Wrapper-Funktion gestartet")
        fun()
        print("Angegebene Funktion dekoriert")
        # Die Wrapper-Funktion fügt der übergebenen Funktion etwas hinzu und der Dekorator
        # gibt die Wrapper-Funktion zurück
    return wrapper_func

@dekorator_func
def sag_tschuess():
    print("Tschüss!!")

sag_tschuess()

Lassen Sie uns alles anhand eines Fallbeispiels durchgehen. Aber bevor wir das tun, sprechen wir über einige OOP-Prinzipien.

Getter und Setter werden in vielen objektorientierten Programmiersprachen verwendet, um das Prinzip der Datenkapselung (das als das Bündeln von Daten mit den Methoden, die auf diese Daten arbeiten, betrachtet wird) sicherzustellen.

Diese Methoden sind natürlich der Getter zum Abrufen der Daten und der Setter zum Ändern der Daten.

Nach diesem Prinzip werden die Attribute einer Klasse privat gemacht, um sie vor anderem Code zu verbergen und zu schützen.

Ja, @property ist im Grunde genommen eine pythonische Art, Getter und Setter zu verwenden.

Python hat ein großartiges Konzept namens Property, das das Leben eines objektorientierten Programmierers viel einfacher macht.

Angenommen, Sie entscheiden sich, eine Klasse zu erstellen, die die Temperatur in Grad Celsius speichern könnte.

class Celsius:
    def __init__(self, temperatur = 0):
        self.set_temperature(temperatur)

    def to_fahrenheit(self):
        return (self.get_temperature() * 1.8) + 32

    def get_temperature(self):
        return self._temperatur

    def set_temperature(self, wert):
        if wert < -273:
            raise ValueError("Temperatur unter -273 ist nicht möglich")
        self._temperatur = wert

Refaktorierter Code, So könnten wir es mit 'Property' erreicht haben.

In Python ist property() eine integrierte Funktion, die ein Property-Objekt erstellt und zurückgibt.

Ein Property-Objekt hat drei Methoden, getter(), setter() und delete().

class Celsius:
    def __init__(self, temperatur = 0):
        self.temperatur = temperatur

    def to_fahrenheit(self):
        return (self.temperatur * 1.8) + 32

    def get_temperature(self):
        print("Wert abrufen")
        return self.temperatur

    def set_temperature(self, wert):
        if wert < -273:
            raise ValueError("Temperatur unter -273 ist nicht möglich")
        print("Wert setzen")
        self.temperatur = wert

temperatur = property(get_temperature,set_temperature)

Hier,

temperatur = property(get_temperature,set_temperature)

könnte aufgeteilt werden als,

# leeres Property erstellen
temperatur = property()
# fget zuweisen
temperatur = temperatur.getter(get_temperature)
# fset zuweisen
temperatur = temperatur.setter(set_temperature)

Zu beachten:

  • get_temperature bleibt ein Property anstelle einer Methode.

Jetzt können Sie auf den Wert von Temperatur zugreifen, indem Sie Folgendes schreiben.

C = Celsius()
C.temperatur
# anstelle von C.get_temperature()

Wir können weitermachen und die Namen get_temperature und set_temperature nicht definieren, da sie unnötig sind und den Klassen-Namespace verschmutzen.

Der pythonische Weg, mit dem obigen Problem umzugehen, besteht darin, @property zu verwenden.

class Celsius:
    def __init__(self, temperatur = 0):
        self.temperatur = temperatur

    def to_fahrenheit(self):
        return (self.temperatur * 1.8) + 32

    @property
    def temperatur(self):
        print("Wert abrufen")
        return self.temperatur

    @temperatur.setter
    def temperatur(self, wert):
        if wert < -273:
            raise ValueError("Temperatur unter -273 ist nicht möglich")
        print("Wert setzen")
        self.temperatur = wert

Zu beachtende Punkte -

  1. Eine Methode, die zum Abrufen eines Werts verwendet wird, ist mit "@property" dekoriert.
  2. Die Methode, die als Setter fungieren soll, ist mit "@temperature.setter" dekoriert. Wenn die Funktion "x" genannt worden wäre, hätten wir sie mit "@x.setter" dekorieren müssen.
  3. Wir haben "zwei" Methoden mit dem gleichen Namen und einer unterschiedlichen Anzahl von Parametern geschrieben, "def temperatur(self)" und "def temperatur(self,x)".

Wie Sie sehen können, ist der Code definitiv weniger elegant.

Jetzt sprechen wir über ein praktisches Szenario im wirklichen Leben.

Angenommen, Sie haben eine Klasse wie folgt entworfen:

class UnsereKlasse:

    def __init__(self, a):
        self.x = a

y = UnsereKlasse(10)
print(y.x)

Nehmen wir weiter an, dass unsere Klasse bei den Kunden beliebt wurde und sie begannen, sie in ihren Programmen zu verwenden. Sie haben alle möglichen Zuweisungen an das Objekt vorgenommen.

Und eines schicksalhaften Tages kam ein vertrauenswürdiger Kunde zu uns und schlug vor, dass "x" ein Wert zwischen 0 und 1000 sein muss; das ist wirklich ein schreckliches Szenario!

Aufgrund von Properties ist es einfach: Wir erstellen eine Property-Version von "x".

class UnsereKlasse:

    def __init__(self,x):
        self.x = x

    @property
    def x(self):
        return self.__x

    @x.setter
    def x(self, x):
        if x < 0:
            self.__x = 0
        elif x > 1000:
            self.__x = 1000
        else:
            self.__x = x

Das ist großartig, oder: Sie können mit der einfachsten denkbaren Implementierung beginnen, und Sie sind frei, später zu einer Property-Version zu migrieren, ohne die Schnittstelle ändern zu müssen! Properties sind also nicht nur ein Ersatz für Getter und Setter!

Sie können diese Implementierung hier überprüfen

31voto

Leo Skhrnkv Punkte 1275

Ich habe alle Beiträge hier gelesen und festgestellt, dass wir möglicherweise ein reales Beispiel brauchen. Warum haben wir eigentlich @property? Also nehmen wir ein Flask-App, in dem Sie ein Authentifizierungssystem verwenden. Sie erklären ein Modell Benutzer in models.py:

class User(UserMixin, db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(64), unique=True, index=True)
    username = db.Column(db.String(64), unique=True, index=True)
    password_hash = db.Column(db.String(128))

    ...

    @property
    def password(self):
        raise AttributeError('password ist kein lesbares Attribut')

    @password.setter
    def password(self, password):
        self.password_hash = generate_password_hash(password)

    def verify_password(self, password):
        return check_password_hash(self.password_hash, password)

In diesem Code haben wir das Attribut Password durch Verwendung von @property "versteckt", was Assertion AttributeError auslöst, wenn Sie versuchen, direkt darauf zuzugreifen, während wir @property.setter verwendet haben, um die tatsächliche Exemplarvariable password_hash zu setzen.

Jetzt können wir in auth/views.py einen Benutzer instanziieren:

...
@auth.route('/register', methods=['GET', 'POST'])
def register():
    form = RegisterForm()
    if form.validate_on_submit():
        user = User(email=form.email.data,
                    username=form.username.data,
                    password=form.password.data)
        db.session.add(user)
        db.session.commit()
...

Beachten Sie das Attribut password, das aus einem Registrierungsformular stammt, wenn ein Benutzer das Formular ausfüllt. Die Passwortbestätigung erfolgt auf der Frontend-Seite mit EqualTo('password', message='Passwörter müssen übereinstimmen') (falls Sie sich fragen, aber es handelt sich um ein anderes Thema im Zusammenhang mit Flask-Formularen).

Ich hoffe, dieses Beispiel wird nützlich sein

20voto

Devendra Bhat Punkte 1059

Dieser Punkt wurde von vielen Leuten da oben geklärt, aber hier ist ein direkter Punkt, den ich gesucht habe. Ich finde es wichtig, mit dem @property-Decorator zu beginnen. z. B.:

class UtilityMixin():
    @property
    def get_config(self):
        return "Das ist eine Eigenschaft"

Der Aufruf der Funktion "get_config()" funktioniert wie folgt:

util = UtilityMixin()
print(util.get_config)

Wenn Sie bemerken, habe ich keine "()" Klammern für den Funktionsaufruf verwendet. Das ist das grundlegende, wonach ich beim @property-Decorator gesucht habe. Dadurch können Sie Ihre Funktion einfach wie eine Variable verwenden.

16voto

Victor Wang Punkte 599

Die beste Erklärung finden Sie hier: Python @Property Erklärt - Wie und Wann verwenden? (Vollständige Beispiele) von Selva Prabhakaran | Veröffentlicht am 5. November 2018

Es half mir zu verstehen WARUM, nicht nur WIE.

https://www.machinelearningplus.com/python/python-property/

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