… обеспечивает пуленепробиваемые интерфейсы.

Первоначально опубликовано на моем частном веб-сайте 10.05.2018 (последнее изменение 12.08.2021).

tl;dr

В Python нет встроенной поддержки Design by Contract (DbC), как в других языках, таких как, например. Д. Однако пакет icontract предоставляет мощную DbCфункциональность для Python. Его функции еще более эффективны, чем
встроенная поддержка DbC в D. Даже если вы не хотите внедрять DbC, знание этой концепции поможет вам написать более надежный код.

Максимально короткое вступление

Проектирование по контракту (DbC), также известное как контрактное программирование, программирование по контракту и программирование по контракту, представляет собой подход к разработке программного обеспечения. Он предписывает разработчикам программного обеспечения определять формальные, точные и проверяемые спецификации интерфейса для компонентов программного обеспечения, которые расширяют обычное определение абстрактных типов данных с предусловиями, постусловиями и инвариантами. — wikipedia.org (Дизайн по контракту)

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

Я узнал о DbC, когда экспериментировал с языком программирования D (преемником C++). D имеет встроенную поддержку DbC, чтобы узнать больше о DbC в D, см. Программирование» книги D».
Python не имеет встроенной поддержки DbC. Существует PEP 316, в котором обсуждалась встроенная поддержка DbC в Python, но она была отложена.

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

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

Когда использовать утверждения DbC?

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

D предоставляет утверждения и исключения, как это делает Python. Не всегда очевидно,
когда использовать DbC условие/инвариант утверждения, а когда генерировать исключения. Утверждения DbC могут не обрабатываться, но можно обрабатывать выброшенные исключения. Подытожим пояснения/рекомендации книги D Раздел «Контрактное программирование» и Раздел «утверждать и применять»:

Предварительные условия

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

Список пожеланий по функциям DbC

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

  • Можно ли включить/отключить DbC?
  • Поддерживаются ли предварительные проверки? (Предварительные условия проверяются перед выполнением функции.)
  • Для каких типов исполняемого кода поддерживаются проверки предварительных условий?
  • → Функции?
  • →__init__ методы?
  • →Методы класса?
  • Поддерживаются ли инвариантные проверки?
  • →Когда выполняются проверки инварианта на уровне класса?
  • → После метода __init__?
  • → Перед выполнением метода открытого класса?
  • →После выполнения метода публичного класса?
  • →После выполнения магического метода?
  • →После установки атрибута класса?
  • Поддерживаются ли проверки постусловия? (Предварительные условия проверяются после выполнения функции.)
  • → Для каких типов исполняемого кода поддерживаются проверки постусловий?
  • →Функции?
  • →__init__ методы?
  • → Методы класса?
  • Как определяются условия и инварианты?
  • →С декораторами?
  • →С аннотациями типов?
  • → Со строками документации?

Зачем нужно включать/отключать DbC?

Контрактное программирование предназначено для защиты от ошибок программиста. Поэтому вы можете предпочесть включить DbC во время разработки и тестирования, но отключить его в рабочей среде. Проверки DbC приводят к накладным расходам во время выполнения. В большинстве случаев накладными расходами можно пренебречь, поскольку они находятся в пределах используемого диапазона на типичной машине разработчика. Тем не менее, это может быть значительным, например. в случае инвариантных проверок для классов, которые обертывают зависимости, такие как удаленные подсистемы, где запрос состояния занимает больше времени. Чтобы уменьшить нагрузку во время выполнения, DbC обычно включается во время разработки и тестирования, но отключается в рабочей среде. Компилируемые языки со встроенной поддержкой, такие как D, обычно используют
флаг компилятора
D Book — Contracts] для включения/отключения ДБК. В Python отключение обычно предоставляется через параметр оптимизации Python -o которые (r)emove(s) утверждают операторы и любой код, зависящий от значения __debug__.

Альтернативы пакета Python

Существуют следующие пакеты (в алфавитном порядке), реализующие функциональность DbC:

Некоторые серьезные недостатки заставляют меня пропускать дальнейшую оценку пакетов. Один из них был, например. «требует написания избыточных описаний условий».

На момент написания я рекомендовал использовать icontract.

заговор

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

  • можно включить/отключить
  • предусловия и постусловия для функций
  • инвариант на классах

Нейтральный:

  • условия оговариваются с декораторами

Недостатки:

  • последний коммит 13 марта 2012 г.
  • требует написания избыточных описаний условий

иметь дело

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

  • последний коммит 7 сентября 2018 г.
  • инвариантные проверки до, после выполнения метода класса и после установки атрибутов

dpcontracts

Недостатки:

  • требует написания избыточных описаний условий

иконтракт

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

  • последний коммит 28 января 2019 г.
  • поддерживает проверки предусловий и постусловий для функций, методов __init__ и методов класса (предусловия и постусловия базовых классов не наследуются)
  • выполнение инвариантной проверки после __init__, перед публичным методом, после публичного метода, после выполнения магического метода *
  • утверждает, что является единственной библиотекой, правильно реализующей унаследованные инварианты с усилением и ослаблением унаследованных инвариантов
  • корректность определения условий и инвариантов можно проверить статическим анализатором pyicontract-lint
  • интегрировать контракты в документацию Sphinx (sphinx-icontract)
  • автоматически генерировать тесты из контрактов (icontract-hypothesis)
  • указать контракт для конечных точек FastAPI RESTful API (fastapi-icontract)

Недостатки:

  • Отключить все проверки можно только при запуске интерпретатора Python в оптимизированном режиме. Можно объявить медленные проверки и отключить их выполнение с помощью переменной окружения.

ПКД

Недостатки:

  • требует написания избыточных описаний условий

pyadbc

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

  • условия/инварианты определяются декораторами
  • инвариант на классах
  • инварианты наследуются
  • поддержка множественного наследования

Нейтральный:

  • условия/инварианты определяются декораторами

Недостатки:

  • последний коммит 18 сентября 2013 г.
  • требует написания избыточных описаний условий

пиконтракт

Нейтральный:

  • условия определяются строками документации

Недостаток:

  • последний коммит от 31 августа 2007 г.

PyContracts

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

  • последний коммит 2 апреля 2019 г.

Недостатки:

  • требует изучения нового синтаксиса
  • требует написания избыточных описаний условий

Спасибо за чтение.

Больше контента на plainenglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку здесь.