Как стать автором
Обновить

Комментарии 16

Самый первый вопрос: стоит ли шкурка вычинки? Стоило ли проделывать такое количество работы ради получения очевидных результатов? Помоему достаточно иметь общее представление о затратах на каждый из методов.

__dict__ — поиск значения в ассоциативном массиве. Что может быть быстрее? Только прямое обращение к атрибуту, если он есть (в противном случае получим «медленное» исключение).

hasattr — реализован через getattr и исключения. Тогда очевидно, что по времени затраты будут примерно такие же как и на отдельную проверку getattr с отловом исключения (при наличие атрибута — быстро, при отсутствие — медленно).

dir — достаточно оценить время выполнения этой функции и сравнить с поиском в ассоциативном массиве.

Вот и получается, что прямой доступ (при наличии атрибута) и __dict__ — самые быстрые методы. dir — сама по себе медленная функция. В остальных случаях, будут выскакивать и ловиться исключения, а это «тяжелый» и медленный механизм. Поэтому, единственное, что заслуживает внимание — это сравнение dir и случаев с исключениями.

Кстати, а как на счет getattr(obj, attr_name, None)? Имхо, если атрибут не найден, это должно работать быстрее, чем бросать исключение.

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

Думаю, стоит. Я не берусь утверждать, что результаты очевидны для всех. Работы в действительно было потрачено немного. А данный тест и даёт общее представление о затратах на каждый из методов — для получения более подробной статистики уже в (полу)готовом приложении можно использовать профайлер :)

__dict__ — поиск значения в ассоциативном массиве. Что может быть быстрее? Только прямое обращение к атрибуту, если он есть...

Хммм… А разве обращение к атрибуту «напрямую» не есть поиск в __dict__?

hasattr — реализован через getattr и исключения. Тогда очевидно, что по времени затраты будут примерно такие же как и на отдельную проверку getattr с отловом исключения (при наличие атрибута — быстро, при отсутствие — медленно).

Если бы hasattr был реализован как getattr с проверкой исключения, то тогда тесты exc_getattr и hasattr показали бы одинаковые результаты — а это не так. Скорее, hasattr проходит по всем __dict__-ам, начиная от экземпляра класса и заканчивая самым «глубоким» объектом класса, осуществляя lookup во всех классах. Возможно, я ошибаюсь — нужно смотреть в исходники Python.

dir — достаточно оценить время выполнения этой функции и сравнить с поиском в ассоциативном массиве.

Я взял эту функцию единственно потому, что в одном проекте, написанном доблестными индусскими программистами, dir() используется именно с этой целью :)

Кстати, а как на счет getattr(obj, attr_name, None)? Имхо, если атрибут не найден, это должно работать быстрее, чем бросать исключение.

Идея хороша, в принципе, но в общем случае неприменима — атрибут может иметь значение None, и тогда будет непонятно, есть ли атрибут или нет.

Но в любом случае, наглядные результаты всегда надежней, чем сухая теория :)

Спасибо :)
Первую часть ответа добавил не туда :(

getattr(obj, attr_name, None) удобно использовать когда нет разницы между отсутствием атрибута и равенством атрибута None.

А вообще, практика показывает, что очень часто, оптимизация кода типа проверка наличия атрибута через getattr и Exception или поиск в __dict__, практически не дает выигрыша в скорости, так как проблемы кроются в самописных алгоритмах или кривости архитектуры :)
Ой, а таки щито ви имеете пготив самописных алгоритмов? :)

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

А getattr(obj, name, None) is not None я проверил:

Групповой зачет:
getattr                  :  15.350072 [0 subtests failed]

Персональный зачет:
test_getattr_true_this_ca                         :   7.596199
test_getattr_true_parent_ca                       :   7.865653
test_getattr_true_this_ia                         :   8.003450
test_getattr_true_parent_ia                       :   8.811715
test_getattr_false                                :  22.630889


Таким образом, видно, что в общем случае getattr лучше прямого запроса свойства и отработку AttributeError, но хуже hasattr. Удивительно, но этот результат противоречит документации, потому что если hasattr реализован через getattr, то они должны были поменяться местами в общем рейтинге.

В персональном зачете getattr показывает себя с лучшей стороны, чем обработка исключения, и, думаю, его можно использовать там, где его можно использовать :) — в отличии от hasattr, он возвращает значение элемента.
Я не против самописных алгоритмов :) Я к тому, что они не всегда сразу получаются оптимальными, поэтому если что-то тормозит, то это в первую очередь будет алгоритм, а не метод доступа к атрибутам (про использование dir я даже не думаю :)
Да, согласен, обращение к атрибуту «напрямую» и есть поиск в __dict__. Но в первом случае должно работать быстрее из-за меньшего количества дополнительных действий, как то получение самого __dict__. Я не утверждаю, а предполагаю.

То, что hasattr реализован через getattr и исключения можно увидеть в доке: «This is implemented by calling getattr(object, name) and seeing whether it raises an exception or not.» При наличие атрибута это видно из тестов test_exc_getattr_true_this_ca и test_hasattr_true_this_ia. Но непонятно, почему так отличается результат в случае отсутствия атрибута (test_hasattr_false и test_hasattr_false).

А насчет получения самого __dict__ — я не помню точно, где и как, но мне встречалась ситуация, где методу __getattribute__(self, name) передавался в качестве name '__dict__'.
Угу, __getattribute__ вызывается при обращении к любым атрибутам, __dict__ в том числе, поэтому первая попытка собственной реализации __getattribute__ обычно заканчивается бесконечной рекурсией :)
OFFTOP: Подскажите, чем делали подсветку кода? Пробовал в своей статье популярный Source Code Highlighter, но он перепахал так, что только маленькие кусочки можно использовать, а большой блок даже не запускается.
Видимо, это повод для следующей статьи. :)
Главный вопрос: а эквивалентны ли эти методы, чтобы можно было сравнивать их скорость?
Как быть когда у вас атрибуты реально не существуют, а эмулируются через __getattr__ / __setattr__?
То есть получится, что в одном случае это будет работать быстрее, в другом существенно медленнеее, а в третьем вообще не будет.
Ммм… я тестировал именно «рафинированные случаи» — производительность самих методов, если считать, что объект не желает странного. Методы — эквивалентны, они всегда, везде, при любых условиях (кроме dir, но это отдельный разговор) возвращают булево значение — существует атрибут или нет. Даже при наличии __getattr__/__getattribute__ эти методы будут работать нормально, если «перехватчики» умеют сваливаться с AttributeError() при эмуляции отсутствии атрибута.

Да, переделал тест на работу с __getattribute__, запустил, жду, пока результаты будут.
Обновил статью.
перенесите, пожалуйста, в блог Python, кармы добавил
а то искал минут 10: как раз понадобилось, и ведь помню, что что-то проскакивало такое, а где искать — непонятно
Перенес, спасибо за карму :-)
спасибо!
положил к себе в закладки, на будущее.

в мозг положил вывод:
иногда можно по разному, но до стадии оптимизации, т.е. почти всегда надо юзать hasattr, но оптимизировать его при желании потом иногда можно
за упоминание dir() в коде реального приложения для целей отличных от дебуга — расстреливать
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории