hasattr () vs try-except блок для работы с несуществующими атрибутами



if hasattr(obj, 'attribute'):
# do somthing


vs



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


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

657   12  

12 ответов:

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

я почти всегда использую 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

плюсы:

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

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

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

  4. меньше try/finally и часто короче, чем hasattr.

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

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

одна вещь, чтобы быть осторожным, если вы заботитесь о том случае, когда obj.attribute имеет значение None, вам нужно будет использовать другое значение sentinel.

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

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

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

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

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

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

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

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

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

первый.

чем короче, тем лучше. Исключения должны быть исключительными.

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

тем не менее, глядя на него с этой точки зрения, тогда это становится вопросом выбора между

try: getattr(obj, attr)
except: ...

и

try: obj.attr
except: ...

С hasattr просто использует первый случай определите результат. Пища для размышлений ;-)

эта тема была освещена в докладе 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

Comments

    Ничего не найдено.