hasattr() против блока try-Кроме того, чтобы иметь дело с несуществующими атрибутами
if hasattr(obj, 'attribute'):
# do somthing
против
try:
# access obj.attribute
except AttributeError, e:
# deal with AttributeError
Что должно быть предпочтительным и почему?
13 ответов
hasattr
внутренне и быстро выполняет ту же задачу, что и try/except
блок: это очень специфический, оптимизированный инструмент для одной задачи, и поэтому его следует отдавать предпочтение, когда это применимо, альтернативе очень общего назначения.
Есть скамейки, которые иллюстрируют разницу в производительности?
время это твой друг
$ python -mtimeit -s 'class C(object): a = 4
c = C()' 'hasattr(c, "nonexistent")'
1000000 loops, best of 3: 1.87 usec per loop
$ python -mtimeit -s 'class C(object): a = 4
c = C()' 'hasattr(c, "a")'
1000000 loops, best of 3: 0.446 usec per loop
$ python -mtimeit -s 'class C(object): a = 4
c = C()' 'try:
c.a
except:
pass'
1000000 loops, best of 3: 0.247 usec per loop
$ python -mtimeit -s 'class C(object): a = 4
c = C()' 'try:
c.nonexistent
except:
pass'
100000 loops, best of 3: 3.13 usec per loop
$
|positive|negative
hasattr| 0.446 | 1.87
try | 0.247 | 3.13
Существует третья, а часто и лучшая альтернатива:
attr = getattr(obj, 'attribute', None)
if attr is not None:
print attr
Преимущества:
getattr
не имеет плохого поведения при проглатывании исключений, на которое указал Мартин Гейзер - в старых питонахhasattr
даже проглотитKeyboardInterrupt
,Обычная причина, по которой вы проверяете, есть ли у объекта атрибут, заключается в том, что вы можете использовать атрибут, и это, естественно, ведет к нему.
Атрибут считывается атомарно и защищен от других потоков, изменяющих объект. (Хотя, если это серьезная проблема, вы можете подумать о блокировке объекта перед тем, как получить к нему доступ.)
Короче чем
try/finally
и часто корочеhasattr
,За рубеж
except AttributeError
блок может поймать другихAttributeErrors
чем тот, который вы ожидаете, что может привести к запутанному поведению.Доступ к атрибуту медленнее, чем к локальной переменной (особенно если это не простой атрибут экземпляра). (Хотя, если честно, микрооптимизация в Python часто является глупым поручением.)
Остерегайтесь одной вещи, если вам небезразличен случай, когда obj.attribute
Нет, вам нужно использовать другое значение часового.
Я почти всегда использую hasattr
: это правильный выбор для большинства случаев.
Проблемный случай, когда класс переопределяет __getattr__
: hasattr
будет ловить все исключения вместо того, чтобы ловить только AttributeError
как вы ожидаете. Другими словами, код ниже будет печатать b: False
хотя было бы более уместно увидеть ValueError
исключение:
class X(object):
def __getattr__(self, attr):
if attr == 'a':
return 123
if attr == 'b':
raise ValueError('important error from your database')
raise AttributeError
x = X()
print 'a:', hasattr(x, 'a')
print 'b:', hasattr(x, 'b')
print 'c:', hasattr(x, 'c')
Таким образом, важная ошибка исчезла. Это было исправлено в Python 3.2 ( выпуск 9666), где hasattr
сейчас только ловит AttributeError
,
Простой обходной путь - написать вспомогательную функцию следующим образом:
_notset = object()
def safehasattr(thing, attr):
return getattr(thing, attr, _notset) is not _notset
Это давайте getattr
разобраться с ситуацией, и тогда может возникнуть соответствующее исключение.
Я бы сказал, что это зависит от того, может ли ваша функция принимать объекты без атрибута, например, если у вас есть два вызывающих объекта, один из которых предоставляет объект с атрибутом, а другой - объект без него.
Если единственный случай, когда вы получите объект без атрибута, вызван какой-то ошибкой, я бы порекомендовал использовать механизм исключений, хотя он может быть медленнее, потому что я считаю, что это более чистый дизайн.
Итог: я думаю, что это проблема дизайна и читабельности, а не эффективности.
Эта тема была освещена в докладе EuroPython 2016 Пишем быстрее Python Себастьяном Витовски. Вот репродукция его слайда с кратким описанием. Он также использует терминологический взгляд, прежде чем вы перейдете к этой дискуссии, стоит упомянуть здесь, чтобы пометить это ключевое слово.
Если атрибут фактически отсутствует, то просить прощения будет медленнее, чем запрашивать разрешения. Так что, как правило, вы можете использовать способ запроса разрешения, если знаете, что весьма вероятно, что атрибут будет отсутствовать или другие проблемы, которые вы можете предсказать. В противном случае, если вы ожидаете, что код будет в большинстве случаев читаемым кодом
3 РАЗРЕШЕНИЯ ИЛИ ПРОЩЕНИЕ?
# CASE 1 -- Attribute Exists
class Foo(object):
hello = 'world'
foo = Foo()
if hasatter(foo, 'hello'):
foo.hello
## 149ns ##
try:
foo.hello
except AttributeError:
pass
## 43.1 ns ##
## 3.5 times faster
# CASE 2 -- Attribute Absent
class Bar(object):
pass
bar = Bar()
if hasattr(bar, 'hello'):
bar.hello
## 428 ns ##
try:
bar.hello
except AttributeError :
pass
## 536 ns ##
## 25% slower
Если отсутствие атрибута не является условием ошибки, у варианта обработки исключительной ситуации есть проблема: он будет также отлавливать AttributeErrors, которые могут возникать изнутри при доступе к obj.attribute (например, потому что атрибут является свойством, так что доступ к нему вызывает некоторый код).
Если вы тестируете только один атрибут, я бы сказал, использовать hasattr
, Однако, если вы делаете несколько обращений к атрибутам, которые могут существовать или не существовать, используйте try
блок может сэкономить вам печатать.
Я бы предложил вариант 2. Вариант 1 имеет условие гонки, если какой-то другой поток добавляет или удаляет атрибут.
Также у python есть идиома, что EAFP ("проще просить прощения, чем разрешения") лучше, чем LBYL ("смотри, прежде чем прыгнуть").
С практической точки зрения, в большинстве языков использование условных выражений всегда будет значительно быстрее обработки исключений.
Если вы хотите обработать случай, когда атрибут не существует где-то за пределами текущей функции, лучше воспользоваться исключением. Индикатором того, что вы можете захотеть использовать исключение вместо условного, является то, что условное условие просто устанавливает флаг и прерывает текущую операцию, и что-то еще проверяет этот флаг и предпринимает действия на его основе.
Тем не менее, как указывает Ракс Олгуд, общение с другими людьми является одним из важных атрибутов кода, и то, что вы хотите сказать, говоря "это исключительная ситуация", а не "это то, чего я ожидаю", может быть более важным,
Первый.
Чем короче, тем лучше. Исключения должны быть исключительными.
По крайней мере, когда дело доходит только до того, что происходит в программе, исключая человеческую часть читабельности и т. Д. (Что на самом деле в большинстве случаев более важно, чем производительность (по крайней мере, в этом случае - с таким диапазоном производительности), как указал Рои Адлер и другие).
Тем не менее, если смотреть на это с этой точки зрения, то становится вопросом выбора между
try: getattr(obj, attr)
except: ...
а также
try: obj.attr
except: ...
поскольку hasattr
просто использует первый случай, чтобы определить результат. Пища для размышлений;-)
Try/Except может быть выбором, когда hasattr() является триггером.
Если вы используете
if hasattr(obj,attr):
# this will do a lookup and in case of @cached_property execute unless
# the attribute already exists
# not to speak about potential race conditions
...
try:
del(self.attr)
except AttributeError:
# ... we're happy
#