Проверка существования участника в 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)