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
блок: это очень специфический, оптимизированный, однозадачный инструмент, и поэтому его следует предпочитать, когда это применимо, альтернативе очень общего назначения.источник
hasattr
будут перехвачены все исключения. См. Мой ответ для примера и тривиального решения.try
могу передать, что операция должна работать. Хотяtry
намерение не всегда таково, оно распространено, поэтому его можно считать более читабельным.Какие-нибудь скамейки, иллюстрирующие разницу в производительности?
время это твой друг
$ 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()
требует поиска имени и вызова функции. На неудачном пути они оба должны выполнить некоторую обработку исключений и agoto
, ноhasattr()
делает это на C, а не на байт-коде Python.Есть третья, часто лучшая альтернатива:
attr = getattr(obj, 'attribute', None) if attr is not None: print attr
Преимущества:
getattr
не имеет плохого поведения, связанного с проглатыванием исключений, на что указал Мартин Гейзер - в старых Pythonshasattr
даже проглотитKeyboardInterrupt
.Обычная причина, по которой вы проверяете, имеет ли объект атрибут, заключается в том, что вы можете использовать атрибут, и это, естественно, приводит к нему.
Атрибут считывается атомарно и защищен от изменений объекта другими потоками. (Хотя, если это является серьезной проблемой, вы можете подумать о блокировке объекта перед доступом к нему.)
Это короче чем
try/finally
и часто короче чемhasattr
.Широкий
except AttributeError
блок может пойматьAttributeErrors
не тот, который вы ожидаете, что может привести к запутанному поведению.Доступ к атрибуту происходит медленнее, чем доступ к локальной переменной (особенно, если это не простой атрибут экземпляра). (Хотя, честно говоря, микрооптимизация в Python часто бывает глупостью.)
Следует быть осторожным, если вам важен случай, когда
obj.attribute
установлено значение None, вам нужно будет использовать другое значение дозорного.источник
Я почти всегда использую
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
и т. Д.safehasattr
, чтобы простоgetattr
скопировать значение в локальную переменную, если вы собираетесь ее использовать, как это почти всегда.hasattr
это было так улучшено.hasattr
, и пошел проверить. У нас было несколько забавных ошибок bzr, когда hasattr просто проглотил ^ C.Я бы сказал, это зависит от того, может ли ваша функция принимать объекты без атрибута по дизайну , например, если у вас есть два вызывающих объекта, один из которых предоставляет объект с атрибутом, а другой - объект без него.
Если единственный случай, когда вы получите объект без атрибута, связан с какой-то ошибкой, я бы рекомендовал использовать механизм исключений, даже если он может быть медленнее, потому что я считаю, что это более чистый дизайн.
Итог: я думаю, что это проблема дизайна и удобочитаемости, а не проблема эффективности.
источник
Если нет атрибута не является условием ошибки, вариант обработки исключений имеет проблему: он также может перехватить AttributeErrors, которые могут возникнуть внутри при доступе к obj.attribute (например, потому что атрибут является свойством, поэтому доступ к нему вызывает некоторый код).
источник
Эта тема была затронута в докладе Себастьяна Витовски на 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
источник
Если вы тестируете только один атрибут, я бы сказал, используйте
hasattr
. Однако, если вы выполняете несколько обращений к атрибутам, которые могут существовать, а могут и не существовать, то использованиеtry
блока может сэкономить вам время на вводе текста.источник
Я бы предложил вариант 2. Вариант 1 имеет состояние гонки, если какой-то другой поток добавляет или удаляет атрибут.
Также у python есть идиома , что EAFP («проще просить прощения, чем разрешение») лучше, чем LBYL («посмотри, прежде чем прыгнуть»).
источник
С практической точки зрения, на большинстве языков использование условного выражения всегда будет значительно быстрее, чем обработка исключения.
Если вы хотите обработать случай, когда атрибут не существует где-то за пределами текущей функции, исключение - лучший способ. Индикатор того, что вы можете захотеть использовать исключение вместо условного, заключается в том, что условное выражение просто устанавливает флаг и прерывает текущую операцию, а что-то в другом месте проверяет этот флаг и предпринимает действия на основе этого.
Тем не менее, как указывает Ракс Ольгуд, общение с другими людьми является одним из важных атрибутов кода, и то, что вы хотите сказать, сказав «это исключительная ситуация», а не «это то, что я ожидаю произойти» может быть более важным. .
источник
Первый.
Короче лучше. Исключения должны быть исключительными.
источник
for
оператора, и оноhasattr
тоже используется. Тем не менее, «короче - лучше» (и «проще - лучше»!) ДЕЙСТВИТЕЛЬНО применяется, поэтому более простой, короткий и конкретный hasattr действительно предпочтительнее.По крайней мере, когда дело касается только того, что происходит в программе, без учета человеческой части читабельности и т. Д. (Что на самом деле в большинстве случаев более важно, чем производительность (по крайней мере, в этом случае - с таким диапазоном производительности), как указали Рои Адлер и другие).
Тем не менее, если посмотреть на это с этой точки зрения, тогда возникает вопрос выбора между
try: getattr(obj, attr) except: ...
и
try: obj.attr except: ...
поскольку
hasattr
для определения результата используется только первый случай. Пища для размышлений ;-)источник