2014-05-02 20 views
7

Sono relativamente nuovo a Python quindi spero che non ho perso qualcosa, ma qui va ..."Private" attribuire proprietà in Python

Sto cercando di scrivere un modulo Python, e io' Mi piacerebbe creare una classe con un attributo "privato" che può (o forse "dovrebbe") essere modificato solo attraverso una o più funzioni all'interno del modulo. Questo è nel tentativo di rendere il modulo più robusto, poiché l'impostazione di questo attributo al di fuori di queste funzioni potrebbe portare a comportamenti indesiderati. Per esempio, potrei avere:

  1. Una classe che memorizza i valori di xey per un grafico a dispersione, Data
  2. Una funzione per leggere valori xey da un file e memorizzarli in classe, read()
  3. Una funzione per tracciare loro, plot()

In questo caso, preferirei se l'utente non è stato in grado di fare qualcosa del genere:

data = Data() 
read("file.csv", data) 
data.x = [0, 3, 2, 6, 1] 
plot(data) 

Mi rendo conto che l'aggiunta di un singolo carattere di sottolineatura principale al nome indica all'utente che l'attributo non deve essere modificato, ovvero rinominare _x e aggiungere un decoratore di proprietà in modo che l'utente possa accedere al valore senza sentirsi in colpa. Tuttavia, quello che se volevo aggiungere un alloggio setter così:

class Data(object): 
    _x = [] 
    _y = [] 
    @property 
    def x(self): 
     return self._x 
    @x.setter 
    def x(self, value): 
     # Do something with value 
     self._x = value 

sono ora nella stessa posizione come ero prima - l'utente può accedere più direttamente l'attributo _x, ma possono ancora impostare utilizzando:

data.x = [0, 3, 2, 6, 1] 

Idealmente mi piacerebbe rinominare le definizioni di funzione proprietà _x(), ma questo porta a confusione su ciò che in realtà significa self._x (a seconda dell'ordine in cui sono dichiarate, questo sembra comportare sia la il setter viene chiamato in modo ricorsivo o il setter viene ignorato a favore dell'attributo).

Un paio di soluzioni che posso pensare:

  1. Aggiungi un doppio underscore all'attributo, __x, in modo che il nome diventa maciullato e non si confonde con la funzione setter. A quanto ho capito, questo dovrebbe essere riservato agli attributi che una classe non desidera condividere con possibili sottoclassi, quindi non sono sicuro che si tratti di un uso legittimo.
  2. Rinominare l'attributo, ad es. _x_stored. Mentre questo risolve completamente il problema, rende il codice più difficile da leggere e introduce i problemi delle convenzioni di denominazione - quali attributi devo rinominare? solo quelli che sono rilevanti? solo quelli che hanno proprietà? solo quelli all'interno di questa classe?

Le soluzioni sopra indicate sono applicabili? E se no, c'è un modo migliore per risolvere questo problema?

Modifica

Grazie per le risposte finora.Alcuni punti gettati dai commenti:

  1. voglio mantenere la logica in più che la proprietà setter mi dà - la sezione # Do something with value nell'esempio di cui sopra - impostando così internamente l'attributo attraverso l'accesso diretto dei self._x non lo fa risolvere il problema.
  2. La rimozione della proprietà setter e la creazione di una funzione separata _set_x() risolve il problema, ma non è una soluzione molto accurata poiché consente di impostare _x in due modi diversi: chiamando tale funzione o tramite l'accesso diretto di self._x. Dovrei quindi tenere traccia di quali attributi dovrebbero essere impostati dalla loro funzione setter (non di proprietà) e che dovrebbero essere modificati attraverso l'accesso diretto. Probabilmente preferirei usare una delle soluzioni che ho suggerito sopra, perché anche se fanno un casino delle convenzioni di denominazione all'interno della classe sono almeno consistenti nel loro uso al di fuori della classe, cioè usano tutte lo zucchero sintattico delle proprietà . Se non c'è modo di farlo in modo più ordinato, suppongo di dover scegliere quello che causa il minimo disturbo.
+0

sono c onfused sul perché vuoi definire un setter che non vuoi che le persone usino. – cmd

+0

@ cmd - Voglio ancora poter utilizzare il setter in altre funzioni all'interno del modulo. Ad esempio, potrei avere una funzione 'interpolate()' che deve essere in grado di impostare i valori x e y. – sftd

+0

Ma quelli possono semplicemente impostare '_x' e' _y'. – kindall

risposta

3

Se si vuole scoraggiare gli utenti di modificare una proprietà, ma vuole che sia chiaro che essi possano leggerlo, mi piacerebbe utilizzare @property senza fornire un setter, simile a quello che hai descritto in precedenza:

class Data(object): 
    def __init__(self): 
     self._x = [] 
     self._y = [] 

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

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

So che mi hai detto "E se avessi voluto aggiungere un setter alla proprietà?", Ma suppongo che lo contrasterei con: Perché aggiungere il setter se non vuoi che i tuoi clienti siano in grado di impostare la proprietà ? Internamente, è possibile accedere direttamente a self._x.

Come per un client che accede direttamente o _x_y, qualsiasi variabile con un prefisso '_' è inteso come "privato" in Python, quindi si dovrebbe fiducia ai vostri clienti di obbedire a questo. Se non obbediscono e finiscono per rovinare tutto, è colpa loro. Questo tipo di mentalità è in contrasto con molti altri linguaggi (C++, Java, ecc.) In cui la riservatezza dei dati è considerata molto importante, ma la cultura di Python è diversa a questo riguardo.

Modifica

Un'altra nota, dal momento che le proprietà private, in questo caso particolare, sono le liste, che sono mutevoli (a differenza di stringhe o interi, che sono immutabili), un client potrebbe finire per cambiarle un po 'per caso:

>>> d = Data() 
>>> print d.x 
['1', '2'] 
>>> l = d.x 
>>> print l 
['1', '2'] 
>>> l.append("3") 
>>> print d.x 
['1', '2', '3'] # Oops! 

Se si vuole evitare questo, si avrebbe bisogno la vostra proprietà per restituire una copia della lista:

@property 
def x(self): 
    return list(self._x) 
+0

Capisco che sia abbastanza semplice impostare internamente il valore usando l'accesso diretto di 'self._x', ma ciò rimuove il vantaggio che mi dà un setter, cioè il bit' # Do qualcosa con valore '. – sftd

+0

Ah, vuoi dire che il setter fa più di un semplice 'self._x = blah'?Immagino che in questo caso, devi anche aggiungere @property alla proprietà privata (che penso tu abbia menzionato che non ti piaceva), o semplicemente ricordarti di chiamare qualche funzione di helper interna quando vuoi assegnarla al privato proprietà. – dano

+0

C'è un modo più elegante per fare ciò che non introduce una convenzione di denominazione incoerente? Si prega di vedere la modifica originale post. (@cmd, @kindall) – sftd