Python isinstance vs hasattr vs try/except: что лучше?

Я пытаюсь выяснить компромиссы между различными подходами, чтобы определить, можно ли с объектом obj выполнить действие do_stuff(). Насколько я понимаю, существует три способа определить, возможно ли это:

# Way 1
if isinstance(obj, Foo):
    obj.do_stuff()

# Way 2
if hasattr(obj, 'do_stuff'):
    obj.do_stuff()

# Way 3
try:
    obj.do_stuff()
except:
    print 'Do something else'

Какой предпочтительный метод (и почему)?

Ответы

Ответ 1

Я считаю, что последний метод обычно предпочитается кодировщиками Python из-за девиза, преподаваемого в сообществе Python: "Легче просить прощения чем разрешение" (EAFP).

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

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


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

Здесь вы хотите записать для AttributeError:

try:
    obj.do_stuff()   # Try to invoke do_stuff
except AttributeError:
    print 'Do something else'  # If unsuccessful, do something else

Ответ 2

Проверка с помощью isinstance выполняется в соответствии с соглашением Python с использованием утиного ввода.

hasattr работает отлично, но Look Before you Leap вместо более Pythonic EAFP.

Ваша реализация пути 3 опасна, так как она ловит любые ошибки, включая те, которые вызваны методом do_stuff. Вы можете пойти более точно:

try:
    _ds = obj.do_stuff
except AttributeError:
    print('Do something else')
else:
    _ds()

Но в этом случае я предпочел бы вариант 2, несмотря на небольшие накладные расходы - это просто более читаемо.

Ответ 3

Правильный ответ "ни" hasattr обеспечивает функциональность, но, возможно, это худший из всех вариантов.

Мы используем объектно-ориентированный характер python, потому что он работает. Анализ OO никогда не бывает точным и часто смущает, но мы используем иерархии классов, потому что знаем, что они помогают людям лучше работать быстрее. Люди захватывают объекты, а хорошая объектная модель помогает кодам быстрее менять и с меньшими ошибками. Правильный код заканчивается кластеризацией в нужных местах. Объекты:

  • Можно просто использовать, не учитывая, какая реализация присутствует.
  • Укажите, что нужно изменить и где
  • Изолировать изменения в некоторой функциональности от изменений к некоторым другим функциям - вы можете исправить X, не опасаясь, что вы сломаете Y

hasattr vs isstance

При использовании isinstance или hasattr вообще указывает, что объектная модель сломана, или мы используем ее неправильно. Правильная вещь - это исправить объектную модель или изменить способ ее использования. Эти две конструкции имеют тот же эффект и в императиве "Мне нужен код для этого смысла, они эквивалентны. Структурно существует огромная разница. При первом приеме этого метода (или через несколько месяцев, когда вы делаете другие вещи), isinstance передает богатство больше информации о том, что происходит, и что еще возможно. Хасаттр ничего не говорит.

Длительная история развития ведет нас от FORTRAN и кода с нагрузками "кто меня переключает". Мы решили использовать объекты, потому что знаем, что они помогают сделать код более удобным для работы. Выбрав hasattr, мы доставляем функциональность, но ничего не исправлено, код более сломан, чем раньше, чем мы начали. При добавлении или изменении этой функциональности в будущем нам придется иметь дело с кодом, который неравномерно сгруппирован и имеет как минимум два принципа организации, некоторые из них там, где это должно быть, а остальное случайным образом разбросано в других местах. Ничего не мешает. Это не одна ошибка, а минное поле потенциальных ошибок, рассеянных по любому пути выполнения, который проходит через ваш hasattr.

Итак, если есть выбор, порядок:

  • Используйте объектную модель или исправьте ее или, по крайней мере, определите, что не так с ним и как его исправить.
  • Использование isinstance
  • Не используйте hasattr