Verificação de existência membro em Python
-
03-07-2019 - |
Pergunta
Eu regularmente querer verificar se um objeto tem um membro ou não. Um exemplo é a criação de um singleton em uma função. Para esse efeito, você pode usar hasattr
assim:
class Foo(object):
@classmethod
def singleton(self):
if not hasattr(self, 'instance'):
self.instance = Foo()
return self.instance
Mas você também pode fazer isso:
class Foo(object):
@classmethod
def singleton(self):
try:
return self.instance
except AttributeError:
self.instance = Foo()
return self.instance
é um método melhor do outro?
Editar: Adicionado o @classmethod
... Mas nota que a questão é não sobre como fazer um singleton, mas como verificar a presença de um membro de um objeto .
Editar: Para que exemplo, um uso típico seria:
s = Foo.singleton()
Então s
é um objeto do tipo Foo
, o mesmo cada vez. E, geralmente, o método é chamado muitas vezes.
Solução
Estas são duas metodologias diferentes:. ?1 é LBYL (olhar antes de saltar) e ?2 é EAFP (mais fácil pedir perdão do que permissão)
Pythonistas tipicamente sugerem que EAFP é melhor, com argumentos em estilo de "what if um processo cria o arquivo entre o momento em que você teste para ele e o tempo que você tentar criá-lo?". Este argumento não se aplica aqui, mas é a idéia geral. Exceções não deve ser tratado como muito excepcional.
Em termos de performance no seu caso -desde a criação de gestores de exceção (a palavra-chave try
) é muito barato em CPython, criando uma exceção (a palavra-chave raise
e criação de exceção interna) é o que é relativamente caros- usando o método ?2 exceção seria criado apenas uma vez; depois, você só usar a propriedade.
Outras dicas
Eu apenas tentei vezes medida:
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
Na minha máquina:
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
Parece que try / except é mais rápido. Também parece mais legível para mim, de qualquer maneira depende do caso, este teste foi muito simples, talvez você precisa de um mais complexo.
Depende de qual caso é "típico", porque as exceções devem modelar, assim, condições atípicas. Assim, se o caso típico é que o atributo instance
deveria existir, em seguida, usar o segundo estilo do código. Se não ter instance
é tão típico como tendo instance
, em seguida, usar o primeiro estilo.
No caso específico de criar um singleton, eu estou inclinado a ir com o primeiro estilo, porque a criação de um singleton o tempo inicial é um caso de uso típico. : -)
Um pouco off-topic na maneira de usá-lo. Singletons são avaliados em excesso, e um método de "estado compartilhado" é tão eficaz, e principalmente, muito limpa em python, por exemplo:
class Borg:
__shared_state = {}
def __init__(self):
self.__dict__ = self.__shared_state
# and whatever else you want in your class -- that's all!
Agora, cada vez que você faz:
obj = Borg()
ela terá a mesma informação, ou, ser um pouco a mesma instância.
Eu tenho que concordar com Chris. Lembre-se, não otimizar até que você realmente precisa para fazê-lo. Eu realmente dúvida a verificação de existência vai ser um gargalo em qualquer programa razoável.
Eu vi http://code.activestate.com/recipes/52558/ como uma maneira de fazer isso também. cópia comentada desse código ( "spam" é apenas um método aleatório a interface de classe tem):
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)