hasattr () против блока try-Кроме того, чтобы иметь дело с несуществующими атрибутами - PullRequest
76 голосов
/ 24 мая 2009
if hasattr(obj, 'attribute'):
    # do somthing

против

try:
    # access obj.attribute
except AttributeError, e:
    # deal with AttributeError

Что должно быть предпочтительнее и почему?

Ответы [ 12 ]

80 голосов
/ 24 мая 2009

Есть ли скамейки, которые иллюстрируют разницу в производительности?

время, это твой друг

$ 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
77 голосов
/ 24 мая 2009

hasattr внутренне и быстро выполняет ту же задачу, что и блок try/except: это очень специфический, оптимизированный инструмент для одной задачи, поэтому его следует отдавать предпочтение, когда это применимо, альтернативе очень общего назначения.

19 голосов
/ 24 мая 2013

Существует третья, а зачастую и лучшая альтернатива:

attr = getattr(obj, 'attribute', None)
if attr is not None:
     print attr

Преимущества:

  1. getattr не имеет плохого поведения по проглатыванию исключений, на которое указал Мартин Гейзер - в старых питонах hasattr даже проглотит KeyboardInterrupt.

  2. Обычная причина, по которой вы проверяете, есть ли у объекта атрибут, заключается в том, что вы можете использовать атрибут, и это, естественно, приводит к нему.

  3. Атрибут считывается атомарно и защищен от других потоков, изменяющих объект. (Хотя, если это серьезная проблема, вы можете подумать о блокировке объекта перед тем, как получить к нему доступ.)

  4. Это короче try/finally и часто короче hasattr.

  5. Широкий except AttributeError блок может поймать AttributeErrors, отличный от ожидаемого, что может привести к путанице в поведении.

  6. Доступ к атрибуту медленнее, чем к локальной переменной (особенно если это не простой атрибут экземпляра). (Хотя, если честно, микрооптимизация в Python - это часто дурацкое поручение.)

Следует быть осторожным, если вам нужен случай, когда для obj.attribute установлено значение Нет, вам нужно использовать другое значение для часового индикатора.

17 голосов
/ 24 апреля 2013

Я почти всегда использую 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 разберемся с ситуацией и затем можем вызвать соответствующее исключение.

13 голосов
/ 24 мая 2009

Я бы сказал, что это зависит от того, может ли ваша функция принимать объекты без атрибута по схеме , например. если у вас есть два вызывающих функции, один из которых предоставляет объект с атрибутом, а другой - без него.

Если единственный случай, когда вы получите объект без атрибута, связан с какой-либо ошибкой, я бы порекомендовал использовать механизм исключений, хотя он может быть медленнее, потому что я считаю, что это более чистый дизайн.

Итог: я думаю, что это проблема дизайна и читабельности, а не эффективности.

4 голосов
/ 30 июня 2009

Если атрибут не имеет , а не условие ошибки, у варианта обработки исключительной ситуации возникает проблема: он также может перехватывать ошибки AttributeErrors, которые могут возникнуть внутри при доступе к obj.attribute экземпляр, потому что атрибут является свойством, так что доступ к нему вызывает некоторый код).

4 голосов
/ 24 мая 2009

Если тестируется только один атрибут, я бы сказал, используйте hasattr. Однако, если вы делаете несколько обращений к атрибутам, которые могут существовать или не существовать, то использование блока try может сэкономить вам набор текста.

3 голосов
/ 24 мая 2009

Я бы предложил вариант 2. Вариант 1 имеет условие гонки, если какой-либо другой поток добавляет или удаляет атрибут.

Также у python есть Идиома , что EAFP («проще просить прощения, чем разрешения») лучше, чем LBYL («смотри, прежде чем прыгнуть»).

2 голосов
/ 16 марта 2018

Этот вопрос был рассмотрен в докладе 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
2 голосов
/ 24 мая 2009

С практической точки зрения, в большинстве языков использование условных выражений всегда будет значительно быстрее обработки исключений.

Если вы хотите обработать случай, когда атрибут не существует где-то за пределами текущей функции, лучше использовать исключение. Индикатором того, что вы можете захотеть использовать исключение вместо условного, является то, что условное условие просто устанавливает флаг и прерывает текущую операцию, а что-то еще проверяет этот флаг и предпринимает действия на его основе.

При этом, как указывает Ракс Олгуд, общение с другими людьми является одним из важных атрибутов кода, и то, что вы хотите сказать, говоря: «это исключительная ситуация», а не «это то, чего я ожидаю», может быть более важным.

...