Verifica dell'esistenza dei membri in Python
-
03-07-2019 - |
Domanda
Voglio controllare regolarmente se un oggetto ha un membro o meno. Un esempio è la creazione di un singleton in una funzione. A tale scopo, puoi utilizzare hasattr
in questo modo:
class Foo(object):
@classmethod
def singleton(self):
if not hasattr(self, 'instance'):
self.instance = Foo()
return self.instance
Ma puoi anche farlo:
class Foo(object):
@classmethod
def singleton(self):
try:
return self.instance
except AttributeError:
self.instance = Foo()
return self.instance
Un metodo è migliore dell'altro?
Modifica: Aggiunto il @classmethod
... Ma nota che la domanda non è non su come creare un singleton ma come controllare la presenza di un membro in un oggetto.
Modifica: Per quell'esempio, un uso tipico sarebbe:
s = Foo.singleton()
Quindi s
è un oggetto di tipo Foo
, lo stesso ogni volta. E, in genere, il metodo viene chiamato molte volte.
Soluzione
Queste sono due diverse metodologie: & # 8470; 1 è LBYL (prima di saltare) e & # 8470; 2 è EAFP (più facile chiedere perdono che autorizzazione).
I pitonisti suggeriscono in genere che EAFP è migliore, con argomenti nello stile di " che cosa succede se un processo crea il file tra il tempo in cui lo provi e il momento in cui provi a crearlo tu stesso? " ;. Questo argomento non si applica qui, ma è l'idea generale. Le eccezioni non dovrebbero essere considerate troppo eccezionali.
Dal punto di vista delle prestazioni nel tuo caso & # 8212; poiché l'impostazione dei gestori di eccezioni (la parola chiave try
) è molto economica in CPython durante la creazione di un'eccezione (la parola chiave raise
e la creazione di eccezioni interne) è ciò che è relativamente costoso & # 8212; usando il metodo & # 8470; 2 l'eccezione verrebbe sollevata una sola volta; successivamente, basta usare la proprietà.
Altri suggerimenti
Ho appena provato a misurare i tempi:
class Foo(object):
@classmethod
def singleton(self):
if not hasattr(self, 'instance'):
self.instance = Foo()
return self.instance
class Bar(object):
@classmethod
def singleton(self):
try:
return self.instance
except AttributeError:
self.instance = Bar()
return self.instance
from time import time
n = 1000000
foo = [Foo() for i in xrange(0,n)]
bar = [Bar() for i in xrange(0,n)]
print "Objs created."
print
for times in xrange(1,4):
t = time()
for d in foo: d.singleton()
print "#%d Foo pass in %f" % (times, time()-t)
t = time()
for d in bar: d.singleton()
print "#%d Bar pass in %f" % (times, time()-t)
print
Sul mio computer:
Objs created.
#1 Foo pass in 1.719000
#1 Bar pass in 1.140000
#2 Foo pass in 1.750000
#2 Bar pass in 1.187000
#3 Foo pass in 1.797000
#3 Bar pass in 1.203000
Sembra che provare / tranne sia più veloce. Mi sembra anche più leggibile, dipende comunque dal caso, questo test è stato molto semplice forse ne avresti bisogno di uno più complesso.
Dipende da quale caso è "tipico", poiché le eccezioni dovrebbero modellare, beh, condizioni atipiche. Pertanto, se il caso tipico è che l'attributo istanza
dovrebbe esistere, utilizzare il secondo stile di codice. Se non avere istanza
è tipico come avere istanza
, utilizzare il primo stile.
Nel caso specifico della creazione di un singleton, sono propenso ad andare con il primo stile, perché la creazione di un singleton il tempo iniziale è un tipico caso d'uso. : -)
Un po 'fuori tema nel modo di usarlo. I singoli sono sopravvalutati e uno "stato condiviso" Il metodo è altrettanto efficace e, soprattutto, molto pulito in Python, ad esempio:
class Borg:
__shared_state = {}
def __init__(self):
self.__dict__ = self.__shared_state
# and whatever else you want in your class -- that's all!
Ora ogni volta che lo fai:
obj = Borg()
avrà le stesse informazioni o, in qualche modo, sarà la stessa istanza.
Devo essere d'accordo con Chris. Ricorda, non ottimizzare fino a quando non è effettivamente necessario. Dubito davvero che verificare l'esistenza sarà un collo di bottiglia in qualsiasi programma ragionevole.
Ho visto http://code.activestate.com/recipes/52558/ anche per farlo. Copia senza commento di quel codice ("spam" è solo un metodo casuale che l'interfaccia di classe ha):
class Singleton:
class __impl:
def spam(self):
return id(self)
__instance = None
def __init__(self):
if Singleton.__instance is None:
Singleton.__instance = Singleton.__impl()
self.__dict__['_Singleton__instance'] = Singleton.__instance
def __getattr__(self, attr):
return getattr(self.__instance, attr)
def __setattr__(self, attr, value):
return setattr(self.__instance, attr, value)