Проверка существования члена в Python - PullRequest
25 голосов
/ 15 октября 2008

Я регулярно хочу проверить, есть ли у объекта член или нет. Примером является создание синглтона в функции. Для этой цели вы можете использовать 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 ]

22 голосов
/ 15 октября 2008

Это две разные методологии: №1 - LBYL (смотри, прежде чем прыгнуть), а №2 - EAFP (проще просить прощения, чем разрешения).

Pythonistas обычно предполагают, что EAFP лучше, с аргументами в стиле «что если процесс создаст файл между временем, когда вы его тестируете, и временем, когда вы пытаетесь создать его самостоятельно?». Этот аргумент здесь не применим, но это общая идея. Исключения не должны рассматриваться как слишком исключительные.

С точки зрения производительности в вашем случае, поскольку настройка менеджеров исключений (ключевое слово try) в CPython обходится очень дешево, а создание исключения (создание ключевого слова raise и создание внутреннего исключения) является относительно дорогим методом №2 исключение будет сделано только один раз; после этого вы просто используете собственность.

10 голосов
/ 15 октября 2008

Я просто пытался измерить время:

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

Кажется, что попытка / исключение происходит быстрее. Мне это также кажется более читабельным, в любом случае, в зависимости от случая, этот тест был очень простым, может быть, вам понадобится более сложный.

5 голосов
/ 15 октября 2008

Это зависит от того, какой случай является «типичным», потому что исключения должны моделировать, ну, в общем, нетипичные условия. Таким образом, если типичным случаем является наличие атрибута instance, используйте второй стиль кода. Если не иметь instance так же типично, как иметь instance, тогда используйте первый стиль.

В конкретном случае создания синглтона я склонен придерживаться первого стиля, поскольку создание синглтона в начальный момент времени является типичным вариантом использования. : -)

1 голос
/ 15 октября 2008

Немного не по теме в использовании. Синглтоны переоценены, и метод «общего состояния» настолько же эффективен и, как правило, очень чист в 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()

у него будет та же информация, или, что-то вроде того же экземпляра.

0 голосов
/ 15 октября 2008

Я должен согласиться с Крисом. Помните, не оптимизируйте, пока вам действительно не понадобится это сделать. Я действительно сомневаюсь, что проверка на существование станет узким местом в любой разумной программе.

Я тоже видел 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)
...