Python @property contro getter e setter



Answers

In Python non usi getter o setter o proprietà solo per il gusto di farlo. Prima devi solo usare gli attributi e poi, solo se necessario, migrare alla proprietà senza dover cambiare il codice usando le tue classi.

Esiste un sacco di codice con estensione .py che usa getter e setter e classi di ereditarietà e inutili ovunque, ad esempio una semplice tupla, ma è il codice di persone che scrivono in C ++ o Java usando Python.

Quello non è il codice Python.

Question

Ecco una domanda di design specifica per Python:

class MyClass(object):
    ...
    def get_my_attr(self):
        ...

    def set_my_attr(self, value):
        ...

e

class MyClass(object):
    ...        
    @property
    def my_attr(self):
        ...

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

Python ci consente di farlo in entrambi i modi. Se dovessi progettare un programma Python, quale approccio useresti e perché?




La risposta breve è: proprietà vince a mani basse. Sempre.

A volte c'è bisogno di getter e setter, ma anche allora, li "nascondo" al mondo esterno. Ci sono molti modi per farlo in Python ( getattr , setattr , __getattribute__ , etc ..., ma uno molto conciso e pulito è:

def set_email(self, value):
    if '@' not in value:
        raise Exception("This doesn't look like an email address.")
    self._email = value

def get_email(self):
    return self._email

email = property(get_email, set_email)

Ecco un breve articolo che introduce l'argomento di getter e setter in Python.




Penso che entrambi abbiano il loro posto. Un problema con l'uso di @property è che è difficile estendere il comportamento di getter o setter in sottoclassi usando meccanismi di classe standard. Il problema è che le funzioni getter / setter sono nascoste nella proprietà.

Puoi effettivamente ottenere le funzioni, ad es. Con

class C(object):
    _p = 1
    @property
    def p(self):
        return self._p
    @p.setter
    def p(self, val):
        self._p = val

è possibile accedere alle funzioni getter e setter come Cpfget e Cpfset , ma non è possibile utilizzare facilmente l'ereditarietà del metodo normale (ad es. super) per estenderle. Dopo aver scavato nella complessità di super, puoi davvero usare super in questo modo:

# Using super():
class D(C):
    # Cannot use super(D,D) here to define the property
    # since D is not yet defined in this scope.
    @property
    def p(self):
        return super(D,D).p.fget(self)

    @p.setter
    def p(self, val):
        print 'Implement extra functionality here for D'
        super(D,D).p.fset(self, val)

# Using a direct reference to C
class E(C):
    p = C.p

    @p.setter
    def p(self, val):
        print 'Implement extra functionality here for E'
        C.p.fset(self, val)

L'utilizzo di super () è, tuttavia, abbastanza complesso, poiché la proprietà deve essere ridefinita, e devi usare il meccanismo leggermente super-intuitivo (cls, cls) per ottenere una copia non associata di p.




Preferirei non usare né nella maggior parte dei casi. Il problema con le proprietà è che rendono la classe meno trasparente. Soprattutto, questo è un problema se si dovesse sollevare un'eccezione da un setter. Ad esempio, se si dispone di una proprietà Account.email:

class Account(object):
    @property
    def email(self):
        return self._email

    @email.setter
    def email(self, value):
        if '@' not in value:
            raise ValueError('Invalid email address.')
        self._email = value

quindi l'utente della classe non si aspetta che l'assegnazione di un valore alla proprietà possa causare un'eccezione:

a = Account()
a.email = 'badaddress'
--> ValueError: Invalid email address.

Di conseguenza, l'eccezione può non essere gestita e propagarsi troppo in alto nella catena di chiamata per essere gestita correttamente, o risultare in un traceback molto inutile presentato all'utente del programma (che è tristemente troppo comune nel mondo di Python e Java) ).

Eviterei anche l'uso di getter e setter:

  • perché definirli in anticipo per tutte le proprietà richiede molto tempo,
  • rende la quantità di codice inutilmente più lunga, il che rende più difficile la comprensione e la manutenzione del codice,
  • se le definissi come proprietà solo se necessario, l'interfaccia della classe cambierebbe, danneggiando tutti gli utenti della classe

Invece di proprietà e getter / setter preferisco fare la logica complessa in posti ben definiti come in un metodo di validazione:

class Account(object):
    ...
    def validate(self):
        if '@' not in self.email:
            raise ValueError('Invalid email address.')

o un metodo Account.save simile.

Nota che non sto cercando di dire che non ci sono casi in cui le proprietà sono utili, solo che potresti stare meglio se puoi rendere le tue lezioni semplici e trasparenti abbastanza da non averne bisogno.




Sono sorpreso che nessuno abbia menzionato che le proprietà sono metodi vincolati di una classe descrittore, e ottengono esattamente questa idea nei loro post - che i getter ei setter sono funzioni e possono essere usati per:

  • convalidare
  • alterare i dati
  • tipo di anatra (tipo di coercizione ad un altro tipo)

Questo presenta un modo intelligente per nascondere dettagli di implementazione e code cruft come espressioni regolari, cast di tipi, try ... eccetto blocchi, asserzioni o valori calcolati.

In generale, fare il CRUD su un oggetto può spesso essere abbastanza banale ma considerare l'esempio di dati che verranno mantenuti in un database relazionale. Gli ORM possono nascondere i dettagli di implementazione di particolari vernacoli SQL nei metodi associati a fget, fset, fdel definiti in una classe di proprietà che gestirà le terribili if .. elif .. else ladder che sono così brutte nel codice OO - esponendo il semplice e elegante self.variable = something e self.variable = something i dettagli per lo sviluppatore utilizzando l'ORM.

Se si considerano le proprietà solo come alcune deprimenti vestigia di un linguaggio Bondage e Discipline (cioè Java), mancano il punto dei descrittori.






Links