Проверка существования члена в Python
Я регулярно хочу проверить, есть ли у объекта член или нет. Примером является создание синглтона в функции. Для этого вы можете использовать hasattr
как это:
class Foo(object):
@classmethod
def singleton(self):
if not hasattr(self, 'instance'):
self.instance = Foo()
return self.instance
Но вы также можете сделать это:
class Foo(object):
@classmethod
def singleton(self):
try:
return self.instance
except AttributeError:
self.instance = Foo()
return self.instance
Один метод лучше другого?
Редактировать: Добавлено @classmethod
... Но учтите, что вопрос не в том, как сделать одиночный объект, а в том, как проверить наличие члена в объекте.
Изменить: для этого примера типичное использование будет:
s = Foo.singleton()
затем s
является объектом типа Foo
, то же самое каждый раз. И, как правило, метод вызывается много раз.
5 ответов
Это две разные методологии: №1 - LBYL (смотри, прежде чем прыгать), а №2 - EAFP (проще просить прощения, чем разрешения).
Pythonistas обычно предполагают, что EAFP лучше, с аргументами в стиле "что если процесс создаст файл между временем, когда вы его тестируете, и временем, когда вы пытаетесь создать его самостоятельно?". Этот аргумент здесь не применим, но это общая идея. Исключения не должны рассматриваться как слишком исключительные.
С точки зрения производительности в вашем случае - с установкой менеджеров исключений (try
ключевое слово) очень дешево в CPython при создании исключения (raise
создание ключевого слова и внутреннего исключения) - это относительно дорого - при использовании метода №2 исключение будет вызвано только один раз; после этого вы просто используете собственность.
Я просто пытался измерить время:
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
На моей машине:
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
Кажется, что попробовать / кроме быстрее. Мне это также кажется более читабельным, в любом случае, это зависит от ситуации, этот тест был очень простым, может быть, вам понадобится более сложный.
Это зависит от того, какой случай является "типичным", потому что исключения должны моделировать, нетипичные условия. Итак, если типичный случай таков, что instance
атрибут должен существовать, затем использовать второй стиль кода. Если не имея instance
так же типично, как instance
Затем используйте первый стиль.
В конкретном случае создания синглтона я склонен придерживаться первого стиля, потому что создание синглтона в начальный момент времени является типичным вариантом использования.:-)
Немного не по теме в использовании. Синглтоны переоценены, а метод "общего состояния" настолько же эффективен и, как правило, очень чист в Python:
class Borg:
__shared_state = {}
def __init__(self):
self.__dict__ = self.__shared_state
# and whatever else you want in your class -- that's all!
Теперь каждый раз, когда вы делаете:
obj = Borg()
у него будет та же информация, или, может быть, несколько такой же экземпляр.
Я должен согласиться с Крисом. Помните, не оптимизируйте, пока вам действительно не понадобится это сделать. Я действительно сомневаюсь, что проверка на существование станет узким местом в любой разумной программе.
Я тоже видел http://code.activestate.com/recipes/52558/ как способ сделать это. Некомментированная копия этого кода ("спам" - это просто случайный метод интерфейса класса):
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)