Ho molte piccole classi diverse che hanno ciascuno alcuni campi, ad es. questo:Come posso esporre i campi di sola lettura dalle classi Python?
class Article:
def __init__(self, name, available):
self.name = name
self.available = available
Qual è il modo più semplice e/o più idiomatico per rendere il campo name
sola lettura, in modo che
a = Article("Pineapple", True)
a.name = "Banana" # <-- should not be possible
non è più possibile?
Ecco quello che ho considerato finora:
Utilizzare un getter (! Ugh).
class Article: def __init__(self, name, available): self._name = name self.available = available def name(self): return self._name
Ugly, non pythonic - e un sacco di codice boilerplate per scrivere (soprattutto se ho più campi per rendere di sola lettura). Tuttavia, fa il lavoro ed è facile capire perché sia così.
Uso
__setattr__
:class Article: def __init__(self, name, available): self.name = name self.available = available def __setattr__(self, name, value): if name == "name": raise Exception("%s property is read-only" % name) self.__dict__[name] = value
sembra piuttosto sul lato chiamante, sembra essere il modo idiomatico per fare il lavoro - ma purtroppo ho molte classi con pochi campi per fare solo ogni lettura. Quindi dovrei aggiungere un'implementazione
__setattr__
a tutti loro. O usare una sorta di mixin forse? In ogni caso, dovrei decidere come comportarmi nel caso in cui un client tenti di assegnare un valore a un campo di sola lettura. Fai qualche eccezione, immagino, ma quale?Utilizzare una funzione di utilità per definire automaticamente le proprietà (e facoltativamente i getter). Questo è fondamentalmente la stessa idea (1), tranne che non scrivo i getter esplicitamente, ma piuttosto fare qualcosa di simile
class Article: def __init__(self, name, available): # This function would somehow give a '_name' field to self # and a 'name()' getter to the 'Article' class object (if # necessary); the getter simply returns self._name defineField(self, "name") self.available = available
Il rovescio della medaglia è che io non so nemmeno se questo è possibile (o come implementarlo) poiché non ho familiarità con la generazione del codice di runtime in Python. :-)
Finora, (2) sembra essere più promettente per me, tranne per il fatto che avrò bisogno __setattr__
definizioni a tutte le mie classi. Vorrei che ci fosse un modo per "annotare" i campi in modo che ciò avvenga automaticamente. Qualcuno ha un'idea migliore?
Per quello che vale, sto cantando Python 2.6.
AGGIORNAMENTO: Grazie per tutte le risposte interessanti! Per ora, ho questo:
def ro_property(o, name, value):
setattr(o.__class__, name, property(lambda o: o.__dict__["_" + name]))
setattr(o, "_" + name, value)
class Article(object):
def __init__(self, name, available):
ro_property(self, "name", name)
self.available = available
Questo sembra funzionare abbastanza bene. Le uniche modifiche necessarie per la classe originale sono
- ho bisogno di ereditare
object
(che non è una cosa così stupida in ogni caso, credo) - ho bisogno di cambiare
self._name = name
-ro_property(self, "name", name)
.
Questo per me è abbastanza bello - qualcuno può vedere un rovescio della medaglia?
Il tuo 'ro_property' è troppo complicato: la risposta di Sven o di Chris è la strada da percorrere. –
@EthanFurman è complicato, ma forse è il prezzo che devi pagare se vuoi mantenere le modifiche apportate alle classi esistenti al minimo? –
Non riesco a ricordare quando ho scritto per ultimo l'attributo di un'istanza (al di fuori dei metodi dell'istanza), tranne quando misero in patch il codice di qualcun altro. Gli attributi di istanza non sono quasi sempre destinati alla sola lettura pratica? Gli attributi di istanza sono in genere impostati in '__init__' e non modificati in seguito, a meno che non si chiami un metodo sull'istanza. Credo che consideri questo stile di codifica appropriato. –