Vérification de l'existence d'un membre en Python
-
03-07-2019 - |
Question
Je veux régulièrement vérifier si un objet a un membre ou non. Un exemple est la création d'un singleton dans une fonction. Pour cela, vous pouvez utiliser hasattr
comme ceci:
class Foo(object):
@classmethod
def singleton(self):
if not hasattr(self, 'instance'):
self.instance = Foo()
return self.instance
Mais vous pouvez aussi faire ceci:
class Foo(object):
@classmethod
def singleton(self):
try:
return self.instance
except AttributeError:
self.instance = Foo()
return self.instance
Une méthode est-elle meilleure que l'autre?
Modifier: Le @classmethod
a été ajouté ... Mais notez que la question est non de savoir comment créer un singleton, mais comment vérifier la présence d'un membre dans un objet.
Modifier: Pour cet exemple, une utilisation typique serait:
s = Foo.singleton()
Alors s
est un objet de type Foo
, identique à chaque fois. Et, généralement, la méthode est appelée plusieurs fois.
La solution
Ce sont deux méthodologies différentes: ?1 est LBYL (à regarder avant de sauter) et ?2 est EAFP (plus facile de demander pardon que l’autorisation).
Les pythonistes suggèrent généralement que EAFP est préférable, avec des arguments dans le style "et si un processus crée le fichier entre le moment où vous le testez et celui où vous essayez de le créer vous-même?". Cet argument ne s'applique pas ici, mais c'est l'idée générale. Les exceptions ne doivent pas être considérées comme aussi exceptionnelles.
En termes de performances, dans votre cas, car la configuration de gestionnaires d'exceptions (le mot clé try
) est très économique dans CPython lors de la création d'une exception (le mot clé raise
et l'exception interne). création) est ce qui est relativement coûteux - avec la méthode ?2, l’exception ne serait levée qu’une fois; ensuite, vous utilisez simplement la propriété.
Autres conseils
Je viens d'essayer de mesurer les temps:
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
Sur ma machine:
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
Il semble que try / except est plus rapide. Cela me semble aussi plus lisible, cela dépend quand même du cas, ce test était très simple, vous auriez peut-être besoin d’un test plus complexe.
Cela dépend du cas "typique", car les exceptions devraient modéliser, ainsi, des conditions atypiques. Ainsi, si le cas typique est que l'attribut instance
devrait exister, utilisez le second style de code. Si ne pas avoir instance
est aussi typique que d'avoir instance
, utilisez le premier style.
Dans le cas spécifique de la création d'un singleton, je suis enclin à choisir le premier style, car la création d'un singleton l'heure initiale est un cas d'utilisation typique. : -)
Un peu hors sujet dans la façon de l'utiliser. Les singletons sont surestimés et un "état partagé" Cette méthode est aussi efficace et surtout très propre en python, par exemple:
class Borg:
__shared_state = {}
def __init__(self):
self.__dict__ = self.__shared_state
# and whatever else you want in your class -- that's all!
Maintenant, chaque fois que vous faites:
obj = Borg()
il aura la même information ou, un peu la même instance.
Je suis d'accord avec Chris. Rappelez-vous, n'optimisez pas avant d'avoir besoin de le faire. Je doute vraiment que le fait de vérifier l'existence existe un goulot d'étranglement dans tout programme raisonnable.
J'ai vu http://code.activestate.com/recipes/52558/ comme un moyen de le faire aussi. Copie non commentée de ce code ("spam" n'est qu'une méthode aléatoire utilisée par l'interface de classe):
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)