Wie funktioniert der @property Dekorateur?


980

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

Dieses Beispiel stammt aus der Dokumentation :

class C(object):
    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, "I'm the 'x' property.")

propertyDie Argumente sind getx:setx , delxund ein doc - String.

Im folgenden Code propertywird als Dekorateur verwendet. Das Objekt davon ist die xFunktion, aber im obigen Code ist in den Argumenten kein Platz für eine Objektfunktion.

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

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

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

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

Und wie entstehen die x.setterund x.deleterDekorateure? Ich bin verwirrt.



3
propertyist eigentlich eine Klasse (keine Funktion), obwohl sie die __init__()Methode wahrscheinlich aufruft , wenn Sie ein Objekt erstellen. Die Verwendung help(property)vom Terminal aus ist aufschlussreich. helpist aus irgendeinem Grund auch eine Klasse.
Brōtsyorfuzthrāx

Ich denke, dieser Link bietet ein gutes Beispiel: [Eigenschaft] ( journaldev.com/14893/python-property-decorator )
Sheng Bi

4
@Shule 2 Jahre alter Thread, aber trotzdem: Alles ist eine Klasse. Sogar Klassen.
Artemis vertraut SE

2
Das war auch für mich verwirrend. Ich habe endlich einen Artikel gefunden, der es für mich aufschlüsseln konnte. Ich hoffe das hilft jemand anderem. programiz.com/python-programming/property Ich bin in keiner Weise mit der Site verbunden.
JJWDesign

Antworten:


1008

Die property()Funktion gibt ein spezielles Deskriptorobjekt zurück :

>>> property()
<property object at 0x10ff07940>

Es ist dieses Objekt, das zusätzliche Methoden hat:

>>> property().getter
<built-in method getter of property object at 0x10ff07998>
>>> property().setter
<built-in method setter of property object at 0x10ff07940>
>>> property().deleter
<built-in method deleter of property object at 0x10ff07998>

Diese wirken als Dekorateure zu . Sie geben ein neues Eigenschaftsobjekt zurück:

>>> property().getter(None)
<property object at 0x10ff079f0>

Das ist eine Kopie des alten Objekts, wobei jedoch eine der Funktionen ersetzt wurde.

Denken Sie daran, dass die @decoratorSyntax nur syntaktischer Zucker ist. die Syntax:

@property
def foo(self): return self._foo

bedeutet wirklich das Gleiche wie

def foo(self): return self._foo
foo = property(foo)

daher wird foodie Funktion ersetzt durch property(foo), was wir oben gesehen haben, ist ein spezielles Objekt. Wenn Sie dann verwenden @foo.setter(), nennen Sie dasproperty().setter Sie die oben gezeigte Methode auf, die eine neue Kopie der Eigenschaft zurückgibt, diesmal jedoch, wobei die Setter-Funktion durch die dekorierte Methode ersetzt wird.

In der folgenden Sequenz wird mithilfe dieser Decorator-Methoden auch eine Full-On-Eigenschaft erstellt.

Zuerst erstellen wir einige Funktionen und ein propertyObjekt mit nur einem Getter:

>>> def getter(self): print('Get!')
... 
>>> def setter(self, value): print('Set to {!r}!'.format(value))
... 
>>> def deleter(self): print('Delete!')
... 
>>> prop = property(getter)
>>> prop.fget is getter
True
>>> prop.fset is None
True
>>> prop.fdel is None
True

Als nächstes verwenden wir die .setter()Methode, um einen Setter hinzuzufügen:

>>> prop = prop.setter(setter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is None
True

Zuletzt fügen wir mit der .deleter()Methode einen Deleter hinzu :

>>> prop = prop.deleter(deleter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is deleter
True

Last but dest nicht das propertyObjekt wirkt als Descriptor - Objekt , so hat es .__get__(), .__set__()und .__delete__()Methoden in dem Instanzattribut einzuzuhaken bekommen, das Setzen und Löschen von :

>>> class Foo: pass
... 
>>> prop.__get__(Foo(), Foo)
Get!
>>> prop.__set__(Foo(), 'bar')
Set to 'bar'!
>>> prop.__delete__(Foo())
Delete!

Das Descriptor Howto enthält eine reine Python-Beispielimplementierung des property()Typs:

class Property:
    "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__)

10
Sehr gut. Sie können die Tatsache hinzufügen, dass Foo.prop = propSie Foo().prop = 5; pront Foo().prop; del Foo().propmit dem gewünschten Ergebnis fertig werden können.
glglgl

12
Methodenobjekte werden im laufenden Betrieb erstellt und können denselben Speicherort wiederverwenden, sofern verfügbar.
Martijn Pieters

1
@MarkusMeskanen: Ich verwende eher type()als Zugriff auf dunder Attribute und Methoden, die von den Standardfunktionen und Operatoren als Erweiterungspunkte verwendet werden sollen.
Martijn Pieters

2
@MarkusMeskanen: Da das Objekt unveränderlich ist und Sie es an Ort und Stelle mutiert haben, können Sie es nicht auf eine Unterklasse spezialisieren.
Martijn Pieters

5
@ MarkusMeskanen: siehe Python überschreibt Getter ohne Setter ; Wenn @human.name.getterdas propertyObjekt an Ort und Stelle human.namegeändert wird, anstatt ein neues zurückzugeben, wird das Attribut geändert, wodurch sich das Verhalten dieser Oberklasse ändert.
Martijn Pieters

201

Die Dokumentation besagt, dass dies nur eine Verknüpfung zum Erstellen schreibgeschützter Eigenschaften ist. Damit

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

ist äquivalent zu

def getx(self):
    return self._x
x = property(getx)

19
Der vollständige Kontext (am besten bewertete Antwort) ist gut, aber diese Antwort war praktisch nützlich, um herauszufinden, warum jemand anderes @propertyin seiner Klasse als Dekorateur gearbeitet hatte.
Ijoseph

1
@property kann auch verwendet werden, wenn Sie einer Klasse ein Attribut hinzufügen möchten und die Kompatibilität mit zuvor erstellten Objekten dieser Klasse beibehalten müssen (z. B. die möglicherweise in einer Pickle-Datei gespeichert sind).
AndyP

111

Hier ist ein minimales Beispiel für @propertydie Implementierung:

class Thing:
    def __init__(self, my_word):
        self._word = my_word 
    @property
    def word(self):
        return self._word

>>> print( Thing('ok').word )
'ok'

Ansonsten wordbleibt eine Methode anstelle einer Eigenschaft.

class Thing:
    def __init__(self, my_word):
        self._word = my_word
    def word(self):
        return self._word

>>> print( Thing('ok').word() )
'ok'

1
Wie würde dieses Beispiel aussehen, wenn die Funktion / Eigenschaft word () in init definiert werden müsste ?
JJ

5
Kann jemand bitte erklären, warum ich hier einen Immobiliendekorateur erstellen würde, anstatt nur self.word = my_word- was dann genauso funktionieren würdeprint( Thing('ok').word ) = 'ok'
SilverSlash

1
@ SilverSlash Dies ist nur ein einfaches Beispiel, ein realer Anwendungsfall würde eine kompliziertere Methode beinhalten
AlexG

Können Sie mir bitte erklären, wie das Drucken Thing('ok').worddie Funktion zur Laufzeit intern aufruft?
Vicrobot

83

Der erste Teil ist einfach:

@property
def x(self): ...

ist das gleiche wie

def x(self): ...
x = property(x)
  • Dies ist wiederum die vereinfachte Syntax zum Erstellen eines propertymit nur einem Getter.

Der nächste Schritt wäre, diese Eigenschaft mit einem Setter und einem Deleter zu erweitern. Und dies geschieht mit den entsprechenden Methoden:

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

Gibt eine neue Eigenschaft zurück, die alles vom alten xund vom angegebenen Setter erbt .

x.deleter funktioniert genauso.


49

Folgendes:

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

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

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

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

Ist das gleiche 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, 
                    "I'm the 'x' property.")

Ist das gleiche 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="I'm the 'x' property.")
    x = x.setter(_x_set)
    x = x.deleter(_x_del)

Ist das gleiche wie:

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

    def _x_get(self):
        return self._x
    x = property(_x_get, doc="I'm the 'x' property.")

    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)

Welches ist das gleiche wie:

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

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

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

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

4
Das erste und das letzte Codebeispiel sind identisch (wörtlich).
Adomas Baliuka

47

Im Folgenden finden Sie ein weiteres Beispiel, wie Sie @propertyhelfen können, wenn Sie den von hier übernommenen Code umgestalten müssen (ich fasse ihn unten nur zusammen):

Stellen Sie sich vor, Sie haben eine Klasse Moneywie diese erstellt:

class Money:
    def __init__(self, dollars, cents):
        self.dollars = dollars
        self.cents = cents

und ein Benutzer erstellt abhängig von dieser Klasse eine Bibliothek, in der er z

money = Money(27, 12)

print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 27 dollar and 12 cents.

Nun nehmen wir an , Sie entscheiden , Ihre Änderungen MoneyKlasse und loszuwerden, die erhalten dollarsund centsAttribute , sondern entscheiden, nur den Gesamtbetrag von Cent zu verfolgen:

class Money:
    def __init__(self, dollars, cents):
        self.total_cents = dollars * 100 + cents

Wenn der oben genannte Benutzer nun versucht, seine Bibliothek wie zuvor auszuführen

money = Money(27, 12)

print("I have {} dollar and {} cents.".format(money.dollars, money.cents))

es wird zu einem Fehler führen

AttributeError: Das Objekt 'Geld' hat kein Attribut 'Dollar'.

Das bedeutet, dass jetzt jeder, der sich auf Ihre ursprüngliche MoneyKlasse verlässt , alle Codezeilen ändern muss, wo dollarsund wo sie centsverwendet werden, was sehr schmerzhaft sein kann ... Wie könnte dies vermieden werden? Mit @property!

Das ist wie:

class Money:
    def __init__(self, dollars, cents):
        self.total_cents = dollars * 100 + cents

    # Getter and setter for dollars...
    @property
    def dollars(self):
        return self.total_cents // 100

    @dollars.setter
    def dollars(self, new_dollars):
        self.total_cents = 100 * new_dollars + self.cents

    # And the getter and setter for cents.
    @property
    def cents(self):
        return self.total_cents % 100

    @cents.setter
    def cents(self, new_cents):
        self.total_cents = 100 * self.dollars + new_cents

wenn wir jetzt aus unserer Bibliothek anrufen

money = Money(27, 12)

print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 27 dollar and 12 cents.

es wird wie erwartet funktionieren und wir mussten keine einzige Codezeile in unserer Bibliothek ändern! Tatsächlich müssten wir nicht einmal wissen, dass sich die Bibliothek, von der wir abhängig sind, geändert hat.

Auch das setterfunktioniert gut:

money.dollars += 2
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 29 dollar and 12 cents.

money.cents += 10
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 29 dollar and 22 cents.

Sie können @propertyauch in abstrakten Klassen verwenden; Ich gebe ein minimales Beispiel hier .


Ihre Zusammenfassung ist sehr gut, das Beispiel, das die Website nimmt, ist ein bisschen seltsam. Ein Anfänger würde fragen. Warum können wir uns nicht einfach an die halten self.dollar = dollars? Wir haben so viel mit @property gemacht, aber es scheint, dass keine Extraktionsfunktion hinzugefügt wurde.
Sheng Bi

1
@ShengBi: Konzentrieren Sie sich nicht so sehr auf das eigentliche Beispiel, sondern mehr auf das zugrunde liegende Prinzip: Wenn Sie - aus welchem ​​Grund auch immer - Code umgestalten müssen, können Sie dies tun, ohne den Code anderer zu beeinflussen.
Cleb

21

Ich habe alle Beiträge hier gelesen und festgestellt, dass wir möglicherweise ein Beispiel aus dem wirklichen Leben brauchen. Warum haben wir eigentlich @property? Stellen Sie sich also eine Flask-App vor, in der Sie das Authentifizierungssystem verwenden. Sie deklarieren einen Modellbenutzer inmodels.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 is not a readable attribute')

    @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 "versteckt" password indem es @propertydie Bestätigung auslöst AttributeError, wenn Sie versuchen, direkt darauf zuzugreifen, während wir @ property.setter verwendet haben, um die tatsächliche Instanzvariable festzulegen password_hash.

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

...
@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()
...

Hinweisattribut password, das aus einem Registrierungsformular stammt, wenn ein Benutzer das Formular ausfüllt. Die Passwortbestätigung erfolgt am Frontend mit EqualTo('password', message='Passwords must match')(falls Sie sich fragen, es sich jedoch um ein anderes themenbezogenes Flask-Formular handelt).

Ich hoffe, dieses Beispiel wird nützlich sein


18

Dieser Punkt wurde von vielen Leuten dort oben geklärt, aber hier ist ein direkter Punkt, den ich gesucht habe. Dies ist meiner Meinung nach wichtig, um mit dem @ property-Dekorateur zu beginnen. z.B:-

class UtilityMixin():
    @property
    def get_config(self):
        return "This is property"

Der Aufruf der Funktion "get_config ()" funktioniert folgendermaßen.

util = UtilityMixin()
print(util.get_config)

Wenn Sie bemerken, dass ich keine "()" Klammern zum Aufrufen der Funktion verwendet habe. Dies ist die grundlegende Sache, nach der ich nach dem @ property-Dekorateur gesucht habe. Damit Sie Ihre Funktion wie eine Variable verwenden können.


1
sehr nützlicher Punkt, der hilft, dieses abstrakte Konzept zu verdichten.
Info5ek

18

Beginnen wir mit Python-Dekoratoren.

Ein Python-Dekorator ist eine Funktion, mit der Sie einer bereits definierten Funktion einige zusätzliche Funktionen hinzufügen können.

In Python ist alles ein Objekt. Funktionen in Python sind erstklassige Objekte, dh sie können von einer Variablen referenziert, in die Listen aufgenommen, als Argumente an eine andere Funktion usw. übergeben werden.

Betrachten Sie das folgende Code-Snippet.

def decorator_func(fun):
    def wrapper_func():
        print("Wrapper function started")
        fun()
        print("Given function decorated")
        # Wrapper function add something to the passed function and decorator 
        # returns the wrapper function
    return wrapper_func

def say_bye():
    print("bye!!")

say_bye = decorator_func(say_bye)
say_bye()

# Output:
#  Wrapper function started
#  bye
#  Given function decorated

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

Python-Syntax für Dekorateur

def decorator_func(fun):
    def wrapper_func():
        print("Wrapper function started")
        fun()
        print("Given function decorated")
        # Wrapper function add something to the passed function and decorator 
        # returns the wrapper function
    return wrapper_func

@decorator_func
def say_bye():
    print("bye!!")

say_bye()

Lassen Sie uns alles abschließen als mit einem Fallszenario, aber vorher sprechen wir über einige Hoppla-Prinzipien.

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

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 privatisiert, um sie zu verbergen und vor anderem Code zu schützen.

Ja, @property ist im Grunde ein pythonische Methode, um Getter und Setter zu verwenden.

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

Nehmen wir an, Sie entscheiden sich für eine Klasse, in der die Temperatur in Grad Celsius gespeichert werden kann.

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

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

def get_temperature(self):
    return self._temperature

def set_temperature(self, value):
    if value < -273:
        raise ValueError("Temperature below -273 is not possible")
    self._temperature = value

Refactored Code, hier ist, wie wir es mit Eigentum erreicht haben könnten.

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

Ein Eigenschaftsobjekt verfügt über drei Methoden: getter (), setter () und delete ().

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

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

def get_temperature(self):
    print("Getting value")
    return self.temperature

def set_temperature(self, value):
    if value < -273:
        raise ValueError("Temperature below -273 is not possible")
    print("Setting value")
    self.temperature = value

temperature = property(get_temperature,set_temperature)

Hier,

temperature = property(get_temperature,set_temperature)

hätte aufgeschlüsselt werden können als,

# make empty property
temperature = property()
# assign fget
temperature = temperature.getter(get_temperature)
# assign fset
temperature = temperature.setter(set_temperature)

Zu beachtender Punkt:

  • get_temperature bleibt eine Eigenschaft anstelle einer Methode.

Jetzt können Sie durch Schreiben auf den Temperaturwert zugreifen.

C = Celsius()
C.temperature
# instead of writing C.get_temperature()

Wir können weiter gehen und die Namen get_temperature und set_temperature nicht definieren, da sie unnötig sind und den Klassennamensraum verschmutzen.

Der pythonische Weg , um mit dem oben genannten Problem umzugehen, ist die Verwendung von @property .

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

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

    @property
    def temperature(self):
        print("Getting value")
        return self.temperature

    @temperature.setter
    def temperature(self, value):
        if value < -273:
            raise ValueError("Temperature below -273 is not possible")
        print("Setting value")
        self.temperature = value

Zu beachtende Punkte -

  1. Eine Methode zum Abrufen eines Werts wird mit "@property" dekoriert.
  2. Die Methode, die als Setter fungieren muss, ist mit "@ temperatur.setter" dekoriert. Wenn die Funktion "x" genannt worden wäre, müssten wir sie mit "@ x.setter" dekorieren.
  3. Wir haben "zwei" Methoden mit demselben Namen und einer unterschiedlichen Anzahl von Parametern "def Temperatur (Selbst)" und "Def Temperatur (Selbst, x)" geschrieben.

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

Lassen Sie uns nun über ein reales praktisches Szenario sprechen.

Angenommen, Sie haben eine Klasse wie folgt entworfen:

class OurClass:

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


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

Nehmen wir nun weiter an, dass unsere Klasse bei Kunden beliebt wurde und sie sie in ihren Programmen verwendeten. Sie haben dem Objekt alle Arten von Aufgaben zugewiesen.

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

Aufgrund von Eigenschaften ist es einfach: Wir erstellen eine Eigenschaftsversion von "x".

class OurClass:

    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, nicht wahr: Sie können mit der einfachsten Implementierung beginnen, die Sie sich vorstellen können, und Sie können später auf eine Eigenschaftsversion migrieren, ohne die Benutzeroberfläche ändern zu müssen! Eigenschaften sind also nicht nur ein Ersatz für Getter und Setter!

Sie können diese Implementierung hier überprüfen


2
Ihre Celsius-Klasse wird sich beim Einstellen unendlich wiederholen (dh bei der Instanziierung).
Ted Petrou

1
@ Ted Petrou Ich habe dich nicht bekommen? Wie wird es beim Einstellen unendlich wiederkehren?
Divyanshu Rawat

Dies ist eigentlich nicht klar ... die Leute fragen warum, aber das Beispiel überzeugt nicht ...
Sheng Bi

1
Es ist nur ein Kommentar, meine persönliche Meinung. Ihre Antwort könnte wirklich gut sein. also lass es.
Sheng Bi

1
Im Vergleich zu den am häufigsten gewählten Antworten ist diese für Menschen konzipiert. Vielen Dank.
Info5ek

6

property ist eine Klasse dahinter @property Dekorateur.

Sie können dies jederzeit überprüfen:

print(property) #<class 'property'>

Ich habe das Beispiel von umgeschrieben, um help(property)zu zeigen, dass die @propertySyntax

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

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

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

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

c = C()
c.x="a"
print(c.x)

ist funktional identisch mit der property()Syntax:

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

    def g(self):
        return self._x

    def s(self, v):
        self._x = v

    def d(self):
        del self._x

    prop = property(g,s,d)

c = C()
c.x="a"
print(c.x)

Wie Sie sehen, gibt es keinen Unterschied, wie wir die Immobilie nutzen.

Zur Beantwortung der Frage wird der @propertyDekorateur über die propertyKlasse implementiert .


Die Frage ist also, die propertyKlasse ein wenig zu erklären . Diese Linie:

prop = property(g,s,d)

War die Initialisierung. Wir können es so umschreiben:

prop = property(fget=g,fset=s,fdel=d)

Die Bedeutung von fget, fsetund fdel:

 |    fget
 |      function to be used for getting an attribute value
 |    fset
 |      function to be used for setting an attribute value
 |    fdel
 |      function to be used for del'ing an attribute
 |    doc
 |      docstring

Das nächste Bild zeigt die Drillinge, die wir aus der Klasse haben property:

Geben Sie hier die Bildbeschreibung ein

__get__, __set__Und __delete__sind dort zu sein , außer Kraft gesetzt . Dies ist die Implementierung des Deskriptormusters in Python.

Im Allgemeinen ist ein Deskriptor ein Objektattribut mit „Bindungsverhalten“, dessen Attributzugriff durch Methoden im Deskriptorprotokoll überschrieben wurde.

Wir können auch verwenden Eigenschaft setter, getterund deleterMethoden , um die Funktion zu Eigentum zu binden. Überprüfen Sie das nächste Beispiel. Die Methode s2der Klasse Csetzt die Eigenschaft verdoppelt .

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

    def g(self):
        return self._x

    def s(self, x):
        self._x = x

    def d(self):
        del self._x

    def s2(self,x):
        self._x=x+x


    x=property(g)
    x=x.setter(s)
    x=x.deleter(d)      


c = C()
c.x="a"
print(c.x) # outputs "a"

C.x=property(C.g, C.s2)
C.x=C.x.deleter(C.d)
c2 = C()
c2.x="a"
print(c2.x) # outputs "aa"

1

Eine Eigenschaft kann auf zwei Arten deklariert werden.

  • Erstellen der Getter, Setter - Methoden für ein Attribut und vorbeifahr dann als Argument an Eigentum - Funktion
  • Verwenden des @ property- Dekorators.

Sie können sich einige Beispiele ansehen, die ich über Eigenschaften in Python geschrieben habe .


Können Sie Ihre Antwort aktualisieren und sagen, dass Eigentum eine Klasse ist, damit ich abstimmen kann?
Prosti


0

Hier ist ein weiteres Beispiel:

##
## Python Properties Example
##
class GetterSetterExample( object ):
    ## Set the default value for x ( we reference it using self.x, set a value using self.x = value )
    __x = None


##
## On Class Initialization - do something... if we want..
##
def __init__( self ):
    ## Set a value to __x through the getter / setter... Since __x is defined above, this doesn't need to be set...
    self.x = 1234

    return None


##
## Define x as a property, ie a getter - All getters should have a default value arg, so I added it - it will not be passed in when setting a value, so you need to set the default here so it will be used..
##
@property
def x( self, _default = None ):
    ## I added an optional default value argument as all getters should have this - set it to the default value you want to return...
    _value = ( self.__x, _default )[ self.__x == None ]

    ## Debugging - so you can see the order the calls are made...
    print( '[ Test Class ] Get x = ' + str( _value ) )

    ## Return the value - we are a getter afterall...
    return _value


##
## Define the setter function for x...
##
@x.setter
def x( self, _value = None ):
    ## Debugging - so you can see the order the calls are made...
    print( '[ Test Class ] Set x = ' + str( _value ) )

    ## This is to show the setter function works.... If the value is above 0, set it to a negative value... otherwise keep it as is ( 0 is the only non-negative number, it can't be negative or positive anyway )
    if ( _value > 0 ):
        self.__x = -_value
    else:
        self.__x = _value


##
## Define the deleter function for x...
##
@x.deleter
def x( self ):
    ## Unload the assignment / data for x
    if ( self.__x != None ):
        del self.__x


##
## To String / Output Function for the class - this will show the property value for each property we add...
##
def __str__( self ):
    ## Output the x property data...
    print( '[ x ] ' + str( self.x ) )


    ## Return a new line - technically we should return a string so it can be printed where we want it, instead of printed early if _data = str( C( ) ) is used....
    return '\n'

##
##
##
_test = GetterSetterExample( )
print( _test )

## For some reason the deleter isn't being called...
del _test.x

Grundsätzlich das gleiche wie im C (Objekt) -Beispiel, außer dass ich stattdessen x verwende ... Ich initialisiere auch nicht in __init - ... nun ja, aber es kann entfernt werden, weil __x als Teil definiert ist der Klasse....

Die Ausgabe ist:

[ Test Class ] Set x = 1234
[ Test Class ] Get x = -1234
[ x ] -1234

und wenn ich self.x = 1234 in init auskommentiere, lautet die Ausgabe:

[ Test Class ] Get x = None
[ x ] None

und wenn ich in der Getter-Funktion _default = None auf _default = 0 setze (da alle Getter einen Standardwert haben sollten, dieser aber nicht von den Eigenschaftswerten übergeben wird, die ich gesehen habe, können Sie ihn hier definieren, und es ist eigentlich nicht schlecht, weil Sie den Standard einmal definieren und überall verwenden können) dh: def x (self, _default = 0):

[ Test Class ] Get x = 0
[ x ] 0

Hinweis: Die Getter-Logik dient nur dazu, den Wert von ihm manipulieren zu lassen, um sicherzustellen, dass er von ihm manipuliert wird - dasselbe gilt für die print-Anweisungen ...

Hinweis: Ich bin an Lua gewöhnt und in der Lage, mehr als 10 Helfer dynamisch zu erstellen, wenn ich eine einzelne Funktion aufrufe, und ich habe etwas Ähnliches für Python erstellt, ohne Eigenschaften zu verwenden, und es funktioniert bis zu einem gewissen Grad, aber obwohl die Funktionen zuvor erstellt wurden Bei der Verwendung gibt es manchmal immer noch Probleme, wenn sie vor dem Erstellen aufgerufen werden, was seltsam ist, da sie nicht so codiert sind. Ich bevorzuge die Flexibilität von Lua-Metatabellen und die Tatsache, dass ich tatsächliche Setter / Getter verwenden kann anstatt im Wesentlichen direkt auf eine Variable zuzugreifen ... Ich mag es, wie schnell einige Dinge mit Python erstellt werden können - zum Beispiel GUI-Programme. Obwohl eine, die ich entwerfe, ohne viele zusätzliche Bibliotheken möglicherweise nicht möglich ist - wenn ich sie in AutoHotkey codiere, kann ich direkt auf die benötigten DLL-Aufrufe zugreifen, und das Gleiche kann in Java, C #, C ++,

Hinweis: Die Code-Ausgabe in diesem Forum ist fehlerhaft - ich musste dem ersten Teil des Codes Leerzeichen hinzufügen, damit es funktioniert - beim Kopieren / Einfügen stellen Sie sicher, dass Sie alle Leerzeichen in Tabulatoren konvertieren. Ich verwende Tabulatoren für Python, weil in Bei einer Datei mit 10.000 Zeilen kann die Dateigröße 512 KB bis 1 MB mit Leerzeichen und 100 bis 200 KB mit Tabulatoren betragen, was einem massiven Unterschied in Bezug auf die Dateigröße und einer Verkürzung der Verarbeitungszeit entspricht.

Tabs können auch pro Benutzer angepasst werden. Wenn Sie also 2 Leerzeichen, 4, 8 oder was auch immer bevorzugen, können Sie dies für Entwickler mit Sehbehinderungen tun.

Hinweis: Alle in der Klasse definierten Funktionen werden aufgrund eines Fehlers in der Forensoftware nicht richtig eingerückt. Stellen Sie sicher, dass Sie sie beim Kopieren / Einfügen einrücken


-3

Eine Bemerkung: Für mich hat Python 2.x @propertynicht wie angekündigt funktioniert, als ich nicht geerbt habe von object:

class A():
    pass

aber funktionierte wenn:

class A(object):
    pass

für Python 3 hat immer funktioniert.


5
Dies liegt daran, dass in Python 2 eine Klasse, von der nicht geerbt objectwird , eine Klasse alten Stils ist und Klassen alten Stils das Deskriptorprotokoll nicht unterstützen (was propertyimplementiert wird, um so zu funktionieren, wie es funktioniert). In Python 3 gibt es keine Klassen im alten Stil mehr. Alle Klassen sind das, was wir in Python 2 als Klassen neuen Stils bezeichnet haben.
chepner
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.