if hasattr(obj, 'attribute'):
# do somthing
vs
try:
# access obj.attribute
except AttributeError, e:
# deal with AttributeError
Что следует предпочесть и почему?
if hasattr(obj, 'attribute'):
# do somthing
vs
try:
# access obj.attribute
except AttributeError, e:
# deal with AttributeError
Что следует предпочесть и почему?
hasattr
внутренне и быстро выполняет ту же задачу, что и блок try/except
: это очень специфический, оптимизированный, однозадачный инструмент, и поэтому его следует предпочитать, когда это применимо, альтернативе очень общего назначения.
hasattr
перехватит все исключения в Python 2.x. См. мой ответ для примера и тривиального обходного пути.
- person Martin Geisler; 24.04.2013
try
может передать, что операция должна работать. Хотя намерение try
не всегда таково, оно распространено, поэтому его можно считать более читабельным.
- person Ioannis Filippidis; 01.02.2014
Есть ли скамейки, демонстрирующие разницу в производительности?
время это твой друг
$ 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
try
примерно в два раза быстрее, чем hasattr()
. В противном случае try
примерно в 1,5 раза медленнее, чем hasattr()
(и оба они существенно медленнее, чем если бы атрибут действительно существовал). Вероятно, это потому, что на счастливом пути try
почти ничего не делает (Python уже оплачивает накладные расходы на исключения независимо от того, используете ли вы их), но hasattr()
требует поиска имени и вызова функции. На неудачном пути им обоим приходится выполнять некоторую обработку исключений и goto
, но hasattr()
делает это на C, а не на байт-коде Python.
- person Kevin; 06.12.2018
Есть третья, и зачастую лучшая альтернатива:
attr = getattr(obj, 'attribute', None)
if attr is not None:
print attr
Преимущества:
getattr
не имеет плохого поведения с проглатыванием исключений, указанного Мартином Гейзером - в старых питонах hasattr
даже проглотит KeyboardInterrupt
.
Обычная причина, по которой вы проверяете, есть ли у объекта атрибут, заключается в том, что вы можете использовать атрибут, и это, естественно, приводит к нему.
Атрибут считывается атомарно и защищен от других потоков, изменяющих объект. (Хотя, если это серьезная проблема, вы можете подумать о блокировке объекта перед доступом к нему.)
Он короче try/finally
и часто короче hasattr
.
Широкий except AttributeError
блок может поймать AttributeErrors
другой блок, отличный от того, который вы ожидаете, что может привести к запутанному поведению.
Доступ к атрибуту происходит медленнее, чем доступ к локальной переменной (особенно, если это не простой атрибут экземпляра). (Хотя, честно говоря, микрооптимизация в Python часто бывает глупостью.)
Следует быть осторожным, если вас интересует случай, когда для obj.attribute
установлено значение None, вам нужно будет использовать другое значение дозорного.
if attr is not None:
. Достаточно только if attr:
.
- person demberto; 04.03.2021
In [1]: class Foo(): ...: def foo(self): ...: return getattr(self, 'x', False) ...: In [2]: f = Foo() In [3]: f.foo() Out[3]: False In [4]: f.x = 1 In [5]: f.foo() Out[5]: 1
великолепно!
- person januszm; 10.06.2021
Я почти всегда использую 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
разобраться с ситуацией и затем вызвать соответствующее исключение.
hasattr
по крайней мере не поймать KeyboardInterrupt
и т. д.
- person poolie; 24.05.2013
safehasattr
, просто используйте getattr
для копирования значения в локальную переменную, если вы собираетесь ее использовать, что вы почти всегда делаете.
- person poolie; 24.05.2013
hasattr
был таким образом улучшен.
- person Martin Geisler; 24.05.2013
hasattr
, и пошел проверить. У нас было несколько забавных ошибок bzr, когда hasattr просто проглотил ^ C.
- person poolie; 24.05.2013
Я бы сказал, это зависит от того, может ли ваша функция принимать объекты без атрибута по дизайну, например если у вас есть два вызывающих объекта, один из которых предоставляет объект с атрибутом, а другой - объект без него.
Если единственный случай, когда вы получите объект без атрибута, связан с какой-то ошибкой, я бы рекомендовал использовать механизм исключений, даже если он может быть медленнее, потому что я считаю, что это более чистый дизайн.
Итог: я думаю, что это проблема дизайна и удобочитаемости, а не проблема эффективности.
Если отсутствие атрибута не является ошибкой, вариант обработки исключений имеет проблему: он также может перехватить AttributeErrors, которые могут появиться внутренне при доступе к obj.attribute (например, потому что атрибут - это свойство, поэтому для доступа к нему требуется некоторый код).
Эта тема была затронута в докладе на EuroPython 2016 Написание более быстрого Python Себастьян Витовски. Вот репродукция его слайда с итогами работы. В этом обсуждении он также использует терминологию посмотрите, прежде чем прыгать, которую стоит упомянуть здесь, чтобы пометить это ключевое слово.
Если атрибут действительно отсутствует, то просьба о прощении будет медленнее, чем запрос разрешений. Итак, как правило, вы можете использовать способ запроса разрешения, если знаете, что очень вероятно, что атрибут будет отсутствовать или другие проблемы, которые вы можете предсказать. В противном случае, если вы ожидаете, что код в большинстве случаев будет читабельным
# 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
Если вы тестируете только один атрибут, я бы сказал, используйте hasattr
. Однако, если вы выполняете несколько обращений к атрибутам, которые могут существовать, а могут и не существовать, использование блока try
может сэкономить вам время на вводе текста.
Я бы предложил вариант 2. Вариант 1 имеет состояние гонки, если какой-то другой поток добавляет или удаляет атрибут.
Также у python есть идиома, что EAFP («проще просить прощения, чем разрешение») лучше чем LBYL («посмотри, прежде чем прыгнуть»).
С практической точки зрения, на большинстве языков использование условного выражения всегда будет значительно быстрее, чем обработка исключения.
Если вы хотите обработать случай, когда атрибут не существует где-то за пределами текущей функции, исключение - лучший способ. Индикатор того, что вы можете захотеть использовать исключение вместо условного, заключается в том, что условное выражение просто устанавливает флаг и прерывает текущую операцию, а что-то в другом месте проверяет этот флаг и предпринимает действия на основе этого.
Тем не менее, как указывает Ракс Ольгуд, общение с другими людьми является одним из важных атрибутов кода, и то, что вы хотите сказать, сказав «это исключительная ситуация», а не «это то, что я ожидаю, должно произойти» может быть более важным. .
Первое.
Короче лучше. Исключения должны быть исключительными.
for
, и hasattr
тоже его использует. Тем не менее, чем короче, тем лучше (а проще - лучше!) ДЕЙСТВИТЕЛЬНО применяется, поэтому более простой, короткий и конкретный hasattr действительно предпочтительнее.
- person Alex Martelli; 24.05.2009
По крайней мере, когда дело касается только того, что происходит в программе, без учета человеческой части читабельности и т. Д. (Что на самом деле в большинстве случаев более важно, чем производительность (по крайней мере, в этом случае - с таким диапазоном производительности), как указали Рои Адлер и другие).
Тем не менее, если посмотреть на это с этой точки зрения, тогда возникает вопрос выбора между
try: getattr(obj, attr)
except: ...
и
try: obj.attr
except: ...
поскольку hasattr
просто использует первый регистр для определения результата. Пища для размышлений ;-)