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