Проверка существования члена в Python
-
03-07-2019 - |
Вопрос
Я регулярно хочу проверить, есть ли у объекта член или нет.Примером может служить создание синглтона в функции.Для этой цели вы можете использовать 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
, каждый раз одно и то же.И, как правило, метод вызывается много раз.
Решение
Это две разные методики:№1 – это LBYL (посмотри, прежде чем прыгнуть) и №2 – EAFP (проще попросить прощения, чем разрешения).
Питонисты обычно предполагают, что 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
Кажется, что try/Exception работает быстрее.Мне он также кажется более читабельным, в любом случае, в зависимости от случая, этот тест был очень простым, возможно, вам понадобится более сложный.
Это зависит от того, какой случай является «типичным», потому что исключения должны моделировать нетипичные условия.Итак, если типичный случай заключается в том, что 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)