Come funziona il decoratore @property?


981

Vorrei capire come funziona la funzione integrata property. Ciò che mi confonde è che propertypuò anche essere usato come decoratore, ma prende argomenti solo quando viene utilizzato come funzione integrata e non quando viene utilizzato come decoratore.

Questo esempio è tratto dalla documentazione :

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.")

property's argomenti sono getx, setx, delxe una stringa doc.

Nel codice seguente propertyviene utilizzato come decoratore. L'oggetto è la xfunzione, ma nel codice sopra non c'è posto per una funzione oggetto negli argomenti.

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

E, come sono le x.settere x.deleterdecoratori creato? Sono confuso.



3
propertyè in realtà una classe (non una funzione), anche se probabilmente chiama il __init__()metodo quando si crea un oggetto, ovviamente. L'utilizzo help(property)dal terminale è approfondito. helpè anche una classe per qualche motivo.
Brōtsyorfuzthrāx l'

Penso che questo link fornisca un buon esempio: [proprietà] ( journaldev.com/14893/python-property-decorator )
Sheng Bi

4
@Shule Discussione di 2 anni, ma ancora: tutto è una classe. Anche le lezioni.
Artemis non si fida ancora del

2
Questo è stato fonte di confusione anche per me. Alla fine ho trovato un articolo che è stato in grado di scomporlo per me. Spero che questo aiuti qualcun'altro. programiz.com/python-programming/property Non sono affiliato in alcun modo al sito.
jjwdesign,

Risposte:


1010

La property()funzione restituisce un oggetto descrittore speciale :

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

È questo oggetto che ha metodi extra :

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

Questi agiscono come decoratori troppo . Restituiscono un nuovo oggetto proprietà:

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

quella è una copia del vecchio oggetto, ma con una delle funzioni sostituite.

Ricorda che la @decoratorsintassi è solo zucchero sintattico; la sintassi:

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

significa davvero la stessa cosa di

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

quindi foola funzione è sostituita da property(foo), che abbiamo visto sopra è un oggetto speciale. Quindi quando usi @foo.setter(), quello che stai facendo è chiamare quel property().settermetodo che ti ho mostrato sopra, che restituisce una nuova copia della proprietà, ma questa volta con la funzione setter sostituita con il metodo decorato.

La seguente sequenza crea anche una proprietà full-on, usando questi metodi di decorazione.

Per prima cosa creiamo alcune funzioni e un propertyoggetto con solo un 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

Quindi usiamo il .setter()metodo per aggiungere un setter:

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

Infine aggiungiamo un deleter con il .deleter()metodo:

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

Ultimo ma non meno importante, l' propertyoggetto agisce come un oggetto descrittore , quindi ha .__get__(), .__set__()e .__delete__()metodi per agganciare in attributo esempio ricevendo, modificando ed eliminazione:

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

Il Descrittore Howto include un'implementazione di esempio Python pura del property()tipo:

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
Molto bene. Potresti aggiungere il fatto che dopo Foo.prop = proppuoi fare Foo().prop = 5; pront Foo().prop; del Foo().propcon il risultato desiderato.
glglgl,

12
Gli oggetti metodo vengono creati al volo e possono riutilizzare la stessa posizione di memoria, se disponibile.
Martijn Pieters

1
@MarkusMeskanen: preferisco usare type()come accesso agli attributi e ai metodi di dunder che devono essere usati come punti di estensione dalle funzioni e dagli operatori standard.
Martijn Pieters

2
@MarkusMeskanen: perché l'oggetto è immutabile e se lo hai trasformato in posizione non potresti specializzarlo in una sottoclasse.
Martijn Pieters

5
@MarkusMeskanen: vedi Python ignorando getter senza setter ; se si @human.name.gettermodifica l' propertyoggetto sul posto anziché restituirne uno nuovo, l' human.nameattributo verrebbe modificato, modificando il comportamento di quella superclasse.
Martijn Pieters

201

La documentazione dice che è solo una scorciatoia per la creazione di proprietà di sola lettura. Così

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

è equivalente a

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

19
Il contesto completo (la risposta più votata) è buono, ma questa risposta è stata praticamente utile per capire perché qualcun altro aveva usato @propertycome decoratore nella loro classe.
ijoseph,

1
@property può anche essere usato quando si desidera aggiungere un attributo a una classe e è necessario mantenere la compatibilità con oggetti precedentemente creati di quella classe (ad es. che potrebbero essere salvati in un file pickle).
AndyP

112

Ecco un esempio minimo di come @propertypuò essere implementato:

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

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

Altrimenti wordrimane un metodo anziché una proprietà.

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

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

1
Come apparirebbe questo esempio se la funzione / proprietà word () dovesse essere definita in init ?
JJ

5
Qualcuno può spiegare perché dovrei creare un decoratore di proprietà qui, invece di avere self.word = my_word- che avrebbe funzionato allo stesso modoprint( Thing('ok').word ) = 'ok'
SilverSlash

1
@SilverSlash Questo è solo un semplice esempio, un caso d'uso reale implicherebbe un metodo più complicato
AlexG

puoi spiegarmi, come stampa Thing('ok').wordchiama la funzione internamente in fase di esecuzione?
Vicrobot il

83

La prima parte è semplice:

@property
def x(self): ...

equivale a

def x(self): ...
x = property(x)
  • che, a sua volta, è la sintassi semplificata per creare un propertycon solo un getter.

Il prossimo passo sarebbe estendere questa proprietà con un setter e un deleter. E questo succede con i metodi appropriati:

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

restituisce una nuova proprietà che eredita tutto dal vecchio xpiù il setter dato.

x.deleter funziona allo stesso modo.


49

Questo seguente:

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

Equivale a:

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.")

Equivale a:

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)

Equivale a:

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)

Che è lo stesso di:

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
Il primo e l'ultimo esempio di codice sono gli stessi (alla lettera).
Adomas Baliuka,

47

Di seguito è riportato un altro esempio su come @propertypuò essere d'aiuto quando si deve refactificare il codice che viene preso da qui (lo riassumo solo di seguito):

Immagina di aver creato una classe Moneycome questa:

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

e un utente crea una libreria in base a questa classe in cui utilizza ad es

money = Money(27, 12)

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

Ora supponiamo che tu decida di cambiare la tua Moneyclasse e di sbarazzarti degli attributi dollarse, centsma invece di tracciare solo il numero totale di centesimi:

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

Se l'utente sopra menzionato ora tenta di eseguire la propria libreria come prima

money = Money(27, 12)

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

comporterà un errore

AttributeError: l'oggetto 'Money' non ha attributo 'dollari'

Ciò significa che ora tutti coloro che si affidano alla tua Moneyclasse originale dovrebbero cambiare tutte le righe di codice dove dollarse centsvengono utilizzate, il che può essere molto doloroso ... Quindi, come si può evitare? Usando @property!

È così che:

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

quando ora chiamiamo dalla nostra biblioteca

money = Money(27, 12)

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

funzionerà come previsto e non abbiamo dovuto modificare una sola riga di codice nella nostra libreria! In effetti, non dovremmo nemmeno sapere che la biblioteca da cui dipendiamo è cambiata.

Anche le setteropere vanno bene:

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.

Puoi usarlo @propertyanche in classi astratte; Faccio un esempio minimo qui .


il tuo riassunto è molto buono, l'esempio che il sito web prende è un po 'strano .. Un principiante potrebbe chiedere .. perché non possiamo semplicemente attenerci self.dollar = dollars? abbiamo fatto così tanto con @property, ma sembra che non sia stata aggiunta alcuna funzionalità di estrazione.
Sheng Bi

1
@ShengBi: non concentrarti molto sull'esempio reale ma più sul principio sottostante: se - per qualsiasi motivo - devi refactificare il codice, puoi farlo senza influenzare il codice di qualcun altro.
Cleb

21

Ho letto tutti i post qui e ho capito che potremmo aver bisogno di un esempio di vita reale. Perché, in realtà, abbiamo @property? Quindi, considera un'app Flask in cui usi il sistema di autenticazione. Dichiara un utente modello 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 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 questo codice abbiamo l'attributo "nascosto" passwordusando @propertyquale attiva l'attivazione AttributeErrorquando si tenta di accedervi direttamente, mentre abbiamo usato @ property.setter per impostare la variabile di istanza effettiva password_hash.

Ora auth/views.pypossiamo istanziare un utente con:

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

Si noti l'attributo passwordche proviene da un modulo di registrazione quando un utente compila il modulo. La conferma della password si verifica sul front-end con EqualTo('password', message='Passwords must match')(nel caso in cui ti stia chiedendo, ma è un argomento diverso relativo ai moduli Flask).

Spero che questo esempio sia utile


18

Questo punto è stato chiarito da molte persone lassù ma qui è un punto diretto che stavo cercando. Questo è ciò che ritengo importante iniziare con il decoratore @property. per esempio:-

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

La chiamata della funzione "get_config ()" funzionerà in questo modo.

util = UtilityMixin()
print(util.get_config)

Se noti che non ho usato le parentesi "()" per chiamare la funzione. Questa è la cosa fondamentale che stavo cercando per il decoratore @property. In modo che tu possa usare la tua funzione proprio come una variabile.


1
punto molto utile che aiuta a condensare questo concetto astratto.
Info5ek,

18

Cominciamo con i decoratori di Python.

Un decoratore Python è una funzione che aiuta ad aggiungere alcune funzionalità aggiuntive a una funzione già definita.

In Python, tutto è un oggetto. Le funzioni in Python sono oggetti di prima classe, il che significa che possono essere referenziati da una variabile, aggiunti negli elenchi, passati come argomenti a un'altra funzione ecc.

Considera il seguente frammento di codice.

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

Qui, possiamo dire che la funzione decoratore ha modificato la nostra funzione say_hello e ha aggiunto alcune righe di codice al suo interno.

Sintassi di Python per decoratore

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

Concludiamo tutto che con uno scenario di caso, ma prima parliamo di alcuni principi di oops.

Getter e setter sono utilizzati in molti linguaggi di programmazione orientati agli oggetti per garantire il principio dell'incapsulamento dei dati (è visto come il raggruppamento di dati con i metodi che operano su questi dati).

Questi metodi sono ovviamente il getter per il recupero dei dati e il setter per la modifica dei dati.

Secondo questo principio, gli attributi di una classe vengono resi privati ​​per nasconderli e proteggerli da altri codici.

, @property è fondamentalmente un modo pitonico per usare getter e setter.

Python ha un grande concetto chiamato proprietà che rende la vita di un programmatore orientato agli oggetti molto più semplice.

Supponiamo che tu decida di creare una classe in grado di memorizzare la temperatura in gradi Celsius.

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

Codice refactored, ecco come avremmo potuto raggiungerlo con la proprietà.

In Python, property () è una funzione integrata che crea e restituisce un oggetto proprietà.

Un oggetto proprietà ha tre metodi, getter (), setter () e 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)

Qui,

temperature = property(get_temperature,set_temperature)

avrebbe potuto essere scomposto come,

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

Nota:

  • get_temperature rimane una proprietà anziché un metodo.

Ora puoi accedere al valore della temperatura scrivendo.

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

Possiamo continuare ulteriormente e non definire i nomi get_temperature e set_temperature poiché non sono necessari e inquinano lo spazio dei nomi della classe.

Il modo pitonico per affrontare il problema sopra è usare @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

Punti da notare -

  1. Un metodo utilizzato per ottenere un valore è decorato con "@property".
  2. Il metodo che deve funzionare come setter è decorato con "@ temperature.setter". Se la funzione fosse stata chiamata "x", dovremmo decorarla con "@ x.setter".
  3. Abbiamo scritto "due" metodi con lo stesso nome e un diverso numero di parametri "def temperature (self)" e "def temperature (self, x)".

Come puoi vedere, il codice è decisamente meno elegante.

Ora parliamo di uno scenario pratico nella vita reale.

Supponiamo che tu abbia progettato una classe come segue:

class OurClass:

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


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

Ora, supponiamo inoltre che la nostra classe sia diventata popolare tra i clienti e che abbiano iniziato a usarlo nei loro programmi. Hanno fatto tutti i tipi di compiti sull'oggetto.

E un giorno fatidico, un cliente fidato è venuto da noi e ha suggerito che "x" deve essere un valore compreso tra 0 e 1000, questo è davvero uno scenario orribile!

Per via delle proprietà è semplice: creiamo una versione di proprietà di "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

È fantastico, non è vero: puoi iniziare con l'implementazione più semplice immaginabile e sei libero di migrare in seguito a una versione di proprietà senza dover cambiare l'interfaccia! Quindi le proprietà non sono solo una sostituzione per getter e setter!

Puoi controllare questa implementazione qui


2
La tua classe Celsius si ripeterà all'infinito durante l'impostazione (il che significa al momento dell'istanza).
Ted Petrou,

1
@Ted Petrou Non ti ho preso? Come si ripeterà all'infinito durante l'impostazione?
Divyanshu Rawat,

Questo in realtà non è chiaro ... la gente si chiede perché, ma l'esempio non è convincente ...
Sheng Bi

1
È solo un commento, la mia opinione personale. La tua risposta potrebbe essere davvero buona. quindi lascialo.
Sheng Bi

1
rispetto alle risposte più votate, questa è progettata per l'uomo; Grazie.
Info5ek,

6

propertyè una classe dietro @propertydecoratore.

Puoi sempre controllare questo:

print(property) #<class 'property'>

Ho riscritto l'esempio da help(property)per mostrare che la @propertysintassi

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)

è funzionalmente identico alla property()sintassi:

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)

Non c'è alcuna differenza nel modo in cui utilizziamo la proprietà come puoi vedere.

Per rispondere alla domanda @propertydecoratore viene implementato tramite propertyclasse.


Quindi, la domanda è spiegare propertyun po ' la classe. Questa linea:

prop = property(g,s,d)

È stata l'inizializzazione. Possiamo riscriverlo in questo modo:

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

Il significato di fget, fsete 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

L'immagine successiva mostra le terzine che abbiamo, della classe property:

inserisci qui la descrizione dell'immagine

__get__, __set__e __delete__sono lì per essere sovrascritti . Questa è l'implementazione del modello descrittore in Python.

In generale, un descrittore è un attributo oggetto con "comportamento vincolante", uno il cui accesso all'attributo è stato sovrascritto da metodi nel protocollo descrittore.

Possiamo anche utilizzare la proprietà setter, gettere deletermetodi per legare la funzione di proprietà. Controlla il prossimo esempio. Il metodo s2della classe Cimposterà la proprietà raddoppiata .

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

Una proprietà può essere dichiarata in due modi.

  • Creare i metodi getter e setter per un attributo e quindi passarli come argomento alla funzione proprietà
  • Utilizzando il decoratore @property .

Puoi dare un'occhiata ad alcuni esempi che ho scritto sulle proprietà in Python .


puoi aggiornare la tua risposta dicendo che la proprietà è una classe in modo che io possa votare.
prosti,


0

Ecco un altro esempio:

##
## 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

Fondamentalmente, lo stesso dell'esempio C (oggetto) tranne che sto usando x invece ... Inoltre non inizializzo in __init - ... beh .. lo faccio, ma può essere rimosso perché __x è definito come parte della classe ....

L'output è:

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

e se commento self.x = 1234 in init, l'output è:

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

e se ho impostato _default = None su _default = 0 nella funzione getter (poiché tutti i getter dovrebbero avere un valore predefinito ma non è passato dai valori delle proprietà da quello che ho visto in modo da poterlo definire qui, e in realtà non è male perché puoi definire il default una volta e usarlo ovunque) cioè: def x (self, _default = 0):

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

Nota: la logica getter è lì solo per farla manipolare il valore per assicurarsi che sia manipolata da esso - lo stesso per le dichiarazioni di stampa ...

Nota: sono abituato a Lua e sono in grado di creare dinamicamente 10+ helper quando chiamo una singola funzione e ho fatto qualcosa di simile per Python senza usare proprietà e funziona fino a un certo punto, ma anche se le funzioni sono state create prima in uso, a volte ci sono ancora problemi con il fatto che vengano chiamati prima della creazione, il che è strano perché non è codificato in questo modo ... Preferisco la flessibilità delle meta-tabelle Lua e il fatto che posso usare setter / getter reali invece di accedere essenzialmente direttamente a una variabile ... Mi piace la velocità con cui alcune cose possono essere costruite con Python, ad esempio i programmi di interfaccia grafica. anche se quello che sto progettando potrebbe non essere possibile senza molte librerie aggiuntive: se lo codifico in AutoHotkey posso accedere direttamente alle chiamate DLL di cui ho bisogno e lo stesso può essere fatto in Java, C #, C ++,

Nota: l'output del codice in questo forum è interrotto - ho dovuto aggiungere spazi alla prima parte del codice per farlo funzionare - quando copia / incolla assicurati di convertire tutti gli spazi in schede .... Uso le schede per Python perché in un file di 10.000 righe la dimensione del file può essere da 512 KB a 1 MB con spazi e da 100 a 200 KB con schede che equivalgono a una differenza enorme per le dimensioni del file e alla riduzione dei tempi di elaborazione ...

Le schede possono anche essere regolate per utente, quindi se preferisci 2 spazi di larghezza, 4, 8 o qualunque cosa tu possa fare, significa che è pensieroso per gli sviluppatori con deficit della vista.

Nota: tutte le funzioni definite nella classe non sono rientrate correttamente a causa di un bug nel software del forum: assicurati di rientrare nel caso in cui copi / incolli


-3

Un'osservazione: per me, per Python 2.x, @propertynon ha funzionato come pubblicizzato quando non ho ereditato da object:

class A():
    pass

ma ha funzionato quando:

class A(object):
    pass

per Python 3, ha funzionato sempre.


5
Questo perché in Python 2, una classe che non eredita objectè una classe vecchio stile e le classi vecchio stile non supportano il protocollo descrittore (che è ciò cheproperty implementa per funzionare nel modo in cui funziona). In Python 3, le classi vecchio stile non esistono più; tutte le classi sono quelle che abbiamo chiamato classi di nuovo stile in Python 2.
chepner
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.