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