if hasattr(obj, 'attribute'):
# do somthing
против
try:
# access obj.attribute
except AttributeError, e:
# deal with AttributeError
Что должно быть предпочтительным и почему?
if hasattr(obj, 'attribute'):
# do somthing
против
try:
# access obj.attribute
except AttributeError, e:
# deal with AttributeError
Что должно быть предпочтительным и почему?
hasattr
внутренне и быстро выполняет ту же задачу, что и блок try/except
: это очень конкретный, оптимизированный инструмент с одной задачей и, следовательно, должен быть предпочтительным, когда это применимо, к самой универсальной альтернативе.
Любые скамейки, которые иллюстрируют разницу в производительности?
timeit it your friend
$ 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
Я почти всегда использую 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 (issue9666) где hasattr
теперь только ловит AttributeError
.
Легкое обходное решение заключается в том, чтобы написать служебную функцию следующим образом:
_notset = object()
def safehasattr(thing, attr):
return getattr(thing, attr, _notset) is not _notset
Это позволит getattr
справиться с ситуацией и затем может поднять соответствующее исключение.
Существует третья, а зачастую и лучшая альтернатива:
attr = getattr(obj, 'attribute', None)
if attr is not None:
print attr
Преимущества:
getattr
не имеет плохого поведения исключения-глотания, указанного Мартином Гейзером - в старых Pythons hasattr
даже проглотит KeyboardInterrupt
.
Обычная причина, по которой вы проверяете, имеет ли объект атрибут, чтобы вы могли использовать этот атрибут, и это, естественно, ведет к нему.
Атрибут считывается атомарно и безопасен для других потоков, изменяющих объект. (Хотя, если это является серьезной проблемой, вы можете рассмотреть возможность блокировки объекта перед его доступом.)
Он короче try/finally
и часто короче hasattr
.
Широкий блок except AttributeError
может поймать другой AttributeErrors
, чем тот, который вы ожидаете, что может привести к запутанному поведению.
Доступ к атрибуту происходит медленнее, чем доступ к локальной переменной (особенно если это не простой атрибут экземпляра). (Хотя, если честно, микро-оптимизация в Python часто является безумным поручением.)
Будьте осторожны, если вам небезразличен случай, когда obj.attribute
установлен в None, вам нужно использовать другое значение часового.
Я бы сказал, что это зависит от того, может ли ваша функция принимать объекты без атрибута по дизайну, например. если у вас есть два вызова функции, одна из которых предоставляет объект с атрибутом, а другой - объект без него.
Если единственный случай, когда вы получите объект без атрибута, вызван некоторой ошибкой, я бы рекомендовал использовать механизм исключений, хотя он может быть медленнее, потому что я считаю, что это более чистый дизайн.
Итог: я думаю, что это проблема дизайна и читаемости, а не проблема эффективности.
Если это только один атрибут, который вы тестируете, я бы сказал, используйте hasattr
. Однако, если вы выполняете несколько обращений к атрибутам, которые могут или не могут существовать, то использование блока try
может сэкономить вам некоторую набранную команду.
С практической точки зрения, в большинстве языков с использованием условного значения всегда будет выполняться быстрее, чем обработка исключения.
Если вы хотите обработать случай с атрибутом, не существующим где-то вне текущей функции, исключение - лучший способ пойти. Индикатором, который вы, возможно, захотите использовать исключение вместо условного, является то, что условие просто устанавливает флаг и прерывает текущую операцию, а что-то в другом месте проверяет этот флаг и предпринимает действия на основе этого.
Тем не менее, как отмечает Ракс Олгуд, общение с другими является одним из важных атрибутов кода, и то, что вы хотите сказать, говоря "это исключительная ситуация", а не "это то, что я ожидаю", может быть более важным.
Я предлагаю вариант 2. Вариант 1 имеет условие гонки, если какой-либо другой поток добавляет или удаляет атрибут.
Также python имеет Idiom, что EAFP ( "проще просить прощения, чем разрешение" ) лучше, чем LBYL ( "смотреть прежде чем прыгать" ).
Если атрибут не не имеет атрибута), у варианта обработки исключений есть проблема: он также поймает атрибуты AttributeErrors, которые могут возникнуть внутри пользователя при доступе к объекту атрибута (например, поскольку атрибут является свойство, чтобы доступ к нему вызывал некоторый код).
Первый.
Короче, лучше. Исключения должны быть исключительными.
По крайней мере, когда дело доходит до того, что происходит в программе, оставляя человеческую часть удобочитаемости и т.д. (что на самом деле в большинстве случаев более интенсивно, чем производительность (по крайней мере в этом случае - с такой производительностью span), как указал Рой Адлер и другие).
Тем не менее, глядя на это с этой точки зрения, тогда становится вопросом выбора между
try: getattr(obj, attr)
except: ...
и
try: obj.attr
except: ...
так как hasattr
просто использует первый случай для определения результата.
Пища для размышлений; -)