Lo sto facendo come:
def set_property(property,value):
def get_property(property):
o
object.property = value
value = object.property
Sono nuovo di Python, quindi sto ancora esplorando la sintassi e vorrei un consiglio su come farlo.
Lo sto facendo come:
def set_property(property,value):
def get_property(property):
o
object.property = value
value = object.property
Sono nuovo di Python, quindi sto ancora esplorando la sintassi e vorrei un consiglio su come farlo.
Risposte:
Prova questo: Proprietà Python
Il codice di esempio è:
class C(object):
def __init__(self):
self._x = None
@property
def x(self):
"""I'm the 'x' property."""
print("getter of x called")
return self._x
@x.setter
def x(self, value):
print("setter of x called")
self._x = value
@x.deleter
def x(self):
print("deleter of x called")
del self._x
c = C()
c.x = 'foo' # setter called
foo = c.x # getter called
del c.x # deleter called
._x
(che non è una proprietà, solo un attributo semplice) ignorano il property
wrapping. Solo riferimenti per .x
passare attraverso property
.
Qual è il modo pitonico di usare getter e setter?
Il modo "Pythonic" non è usare "getter" e "setter", ma usare attributi semplici, come dimostra la domanda, e del
per cancellarli (ma i nomi vengono cambiati per proteggere gli innocenti ... builtin):
value = 'something'
obj.attribute = value
value = obj.attribute
del obj.attribute
Se in seguito, vuoi modificare l'impostazione e ottenere, puoi farlo senza dover modificare il codice utente, usando il property
decoratore:
class Obj:
"""property demo"""
#
@property # first decorate the getter method
def attribute(self): # This getter method name is *the* name
return self._attribute
#
@attribute.setter # the property decorates with `.setter` now
def attribute(self, value): # name, e.g. "attribute", is the same
self._attribute = value # the "value" name isn't special
#
@attribute.deleter # decorate with `.deleter`
def attribute(self): # again, the method name is the same
del self._attribute
(Ogni utilizzo del decoratore copia e aggiorna l'oggetto della proprietà precedente, pertanto è necessario utilizzare lo stesso nome per ogni set, ottenere ed eliminare la funzione / metodo.
Dopo aver definito quanto sopra, l'impostazione originale, il recupero e l'eliminazione del codice sono gli stessi:
obj = Obj()
obj.attribute = value
the_value = obj.attribute
del obj.attribute
Dovresti evitare questo:
def set_property(property,value): def get_property(property):
Innanzitutto, quanto sopra non funziona, perché non fornisci un argomento per l'istanza su cui la proprietà verrebbe impostata (di solito self
), che sarebbe:
class Obj:
def set_property(self, property, value): # don't do this
...
def get_property(self, property): # don't do this either
...
In secondo luogo, questo duplica lo scopo di due metodi speciali __setattr__
e __getattr__
.
In terzo luogo, abbiamo anche le setattr
e getattr
incorporate funzioni.
setattr(object, 'property_name', value)
getattr(object, 'property_name', default_value) # default is optional
Il @property
decoratore serve per creare getter e setter.
Ad esempio, potremmo modificare il comportamento dell'impostazione per porre restrizioni al valore impostato:
class Protective(object):
@property
def protected_value(self):
return self._protected_value
@protected_value.setter
def protected_value(self, value):
if acceptable(value): # e.g. type or range check
self._protected_value = value
In generale, vogliamo evitare di usare property
e usare solo attributi diretti.
Questo è ciò che si aspettano gli utenti di Python. Seguendo la regola della minima sorpresa, dovresti provare a dare ai tuoi utenti ciò che si aspettano, a meno che tu non abbia una ragione molto convincente del contrario.
Ad esempio, supponiamo di aver bisogno che l'attributo protetto del nostro oggetto sia un numero intero compreso tra 0 e 100 inclusi e ne prevenga l'eliminazione, con messaggi appropriati per informare l'utente del suo corretto utilizzo:
class Protective(object):
"""protected property demo"""
#
def __init__(self, start_protected_value=0):
self.protected_value = start_protected_value
#
@property
def protected_value(self):
return self._protected_value
#
@protected_value.setter
def protected_value(self, value):
if value != int(value):
raise TypeError("protected_value must be an integer")
if 0 <= value <= 100:
self._protected_value = int(value)
else:
raise ValueError("protected_value must be " +
"between 0 and 100 inclusive")
#
@protected_value.deleter
def protected_value(self):
raise AttributeError("do not delete, protected_value can be set to 0")
(Si noti che si __init__
riferisce a self.protected_value
ma i metodi di proprietà fanno riferimento self._protected_value
. Questo è così che __init__
utilizza la proprietà attraverso l'API pubblica, assicurandosi che sia "protetta".)
E utilizzo:
>>> p1 = Protective(3)
>>> p1.protected_value
3
>>> p1 = Protective(5.0)
>>> p1.protected_value
5
>>> p2 = Protective(-5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __init__
File "<stdin>", line 15, in protected_value
ValueError: protectected_value must be between 0 and 100 inclusive
>>> p1.protected_value = 7.3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 17, in protected_value
TypeError: protected_value must be an integer
>>> p1.protected_value = 101
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 15, in protected_value
ValueError: protectected_value must be between 0 and 100 inclusive
>>> del p1.protected_value
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 18, in protected_value
AttributeError: do not delete, protected_value can be set to 0
Sì lo fanno . .setter
e .deleter
fare copie della proprietà originale. Ciò consente alle sottoclassi di modificare correttamente il comportamento senza alterare il comportamento nel genitore.
class Obj:
"""property demo"""
#
@property
def get_only(self):
return self._attribute
#
@get_only.setter
def get_or_set(self, value):
self._attribute = value
#
@get_or_set.deleter
def get_set_or_delete(self):
del self._attribute
Ora per far funzionare tutto questo, devi usare i rispettivi nomi:
obj = Obj()
# obj.get_only = 'value' # would error
obj.get_or_set = 'value'
obj.get_set_or_delete = 'new value'
the_value = obj.get_only
del obj.get_set_or_delete
# del obj.get_or_set # would error
Non sono sicuro di dove sarebbe utile, ma il caso d'uso è se si desidera una proprietà get, set e / o delete-only. Probabilmente è meglio attenersi alla stessa proprietà semanticamente con lo stesso nome.
Inizia con attributi semplici.
Se in seguito hai bisogno di funzionalità attorno all'impostazione, alla ricezione e alla cancellazione, puoi aggiungerla con il decoratore di proprietà.
Evita le funzioni denominate set_...
e get_...
: ecco a cosa servono le proprietà.
__init__
metodo si riferisce a self.protected_value
ma il getter e setter si riferiscono self._protected_value
. Potresti spiegare come funziona? Ho testato il tuo codice e funziona così com'è, quindi questo non è un errore di battitura.
__init__
esso?
self.protected_value = start_protected_value
realtà sta chiamando la funzione setter; Ho pensato che fosse un compito.
In [1]: class test(object):
def __init__(self):
self.pants = 'pants'
@property
def p(self):
return self.pants
@p.setter
def p(self, value):
self.pants = value * 2
....:
In [2]: t = test()
In [3]: t.p
Out[3]: 'pants'
In [4]: t.p = 10
In [5]: t.p
Out[5]: 20
L'utilizzo @property
e @attribute.setter
ti aiuta non solo a utilizzare il modo "pitonico", ma anche a verificare la validità degli attributi sia durante la creazione dell'oggetto che durante la sua modifica.
class Person(object):
def __init__(self, p_name=None):
self.name = p_name
@property
def name(self):
return self._name
@name.setter
def name(self, new_name):
if type(new_name) == str: #type checking for name property
self._name = new_name
else:
raise Exception("Invalid value for name")
Con questo, in realtà 'nascondi' _name
attributo dagli sviluppatori client ed esegui anche controlli sul tipo di proprietà del nome. Si noti che seguendo questo approccio anche durante l'iniziazione viene chiamato il setter. Così:
p = Person(12)
Porterà a:
Exception: Invalid value for name
Ma:
>>>p = person('Mike')
>>>print(p.name)
Mike
>>>p.name = 'George'
>>>print(p.name)
George
>>>p.name = 2.3 # Causes an exception
Dai un'occhiata al @property
decoratore .
Puoi usare accessori / mutatori (cioè @attr.setter
e @property
) oppure no, ma la cosa più importante è essere coerenti!
Se stai usando @property
per accedere semplicemente ad un attributo, ad es
class myClass:
def __init__(a):
self._a = a
@property
def a(self):
return self._a
usalo per accedere ad ogni * attributo! Sarebbe una cattiva pratica accedere ad alcuni attributi usando @property
e lasciare pubbliche altre proprietà (es. Nome senza trattino basso) senza un accedente, ad es . Non farlo
class myClass:
def __init__(a, b):
self.a = a
self.b = b
@property
def a(self):
return self.a
Si noti che qui self.b
non è presente un accesso esplicito anche se è pubblico.
Analogamente ai setter (o ai mutatori ), sentiti libero di usare @attribute.setter
ma sii coerente! Quando lo fai ad es
class myClass:
def __init__(a, b):
self.a = a
self.b = b
@a.setter
def a(self, value):
return self.a = value
È difficile per me indovinare la tua intenzione. Da un lato si sta dicendo che sia a
e b
sono pubblici (nessuna sottolineatura leader nel loro nome) quindi dovrei essere teoricamente permesso di accesso / mutare (get / set) entrambi. Ma poi specifichi un mutatore esplicito solo per a
, il che mi dice che forse non dovrei essere in grado di impostare b
. Dato che hai fornito un mutatore esplicito, non sono sicuro che la mancanza di un esplicito accessor ( @property
) significhi che non dovrei essere in grado di accedere a nessuna di queste variabili o che tu fossi semplicemente frugale nell'utilizzare @property
.
* L'eccezione è quando si desidera rendere esplicitamente accessibili o modificabili alcune variabili ma non entrambe o si desidera eseguire una logica aggiuntiva quando si accede o si modifica un attributo. Questo è quando sto usando personalmente @property
e @attribute.setter
(altrimenti nessun esplicito acessor / mutatore per attributi pubblici).
Infine, suggerimenti PEP8 e Google Style Guide:
PEP8, Designing for Inheritance dice:
Per semplici attributi di dati pubblici, è meglio esporre solo il nome dell'attributo, senza complicati metodi di accesso / mutatore . Tieni presente che Python fornisce un percorso semplice per il miglioramento futuro, se ritieni che un semplice attributo di dati debba aumentare il comportamento funzionale. In tal caso, utilizzare le proprietà per nascondere l'implementazione funzionale dietro la sintassi di accesso agli attributi di dati semplici.
D'altra parte, secondo la Guida di stile di Google Regole / proprietà del linguaggio Python, la raccomandazione è di:
Usa le proprietà nel nuovo codice per accedere o impostare i dati in cui normalmente avresti utilizzato metodi di accesso o setter semplici e leggeri. Le proprietà dovrebbero essere create con il
@property
decoratore.
I pro di questo approccio:
La leggibilità aumenta aumentando il richiamo esplicito e impostando le chiamate di metodo per un semplice accesso agli attributi. Consente ai calcoli di essere pigri. Considerato il modo Pythonic per mantenere l'interfaccia di una classe. In termini di prestazioni, consentire le proprietà elude la necessità di metodi di accesso banali quando è ragionevole un accesso diretto alla variabile. Ciò consente anche di aggiungere metodi di accesso in futuro senza interrompere l'interfaccia.
e contro:
Deve ereditare da
object
in Python 2. Può nascondere effetti collaterali come il sovraccarico dell'operatore. Può essere fonte di confusione per le sottoclassi.
@property
, fare anche il resto @property
sembra una decisione sbagliata.
@property
(ad es. Eseguendo una logica speciale prima di restituire un attributo). Altrimenti perché dovresti decorare un attributo con @propery
e non altri?
@property
per cominciare, giusto? Se il tuo getter lo è return this._x
e il tuo setter lo è this._x = new_x
, allora l'utilizzo @property
è un po 'sciocco.
@property
è la coerenza".
Puoi usare i metodi magici __getattribute__
e __setattr__
.
class MyClass:
def __init__(self, attrvalue):
self.myattr = attrvalue
def __getattribute__(self, attr):
if attr == "myattr":
#Getter for myattr
def __setattr__(self, attr):
if attr == "myattr":
#Setter for myattr
Sii consapevole __getattr__
e __getattribute__
non sono gli stessi. __getattr__
viene invocato solo quando l'attributo non viene trovato.