Подтвердить что ты не робот

Использование проекта по контракту в Python

Я ищу, чтобы начать использовать DBC на большом количестве проектов на основе Python на работе, и я задаюсь вопросом, что у других было с ним. До сих пор мое исследование показало следующее:

  • http://www.python.org/dev/peps/pep-0316/ - PEP 316, который должен стандартизировать проект по контракту на Python, который был отложен. Этот PEP предлагает использовать docstrings.
  • http://www.wayforward.net/pycontract/ - Контракты для Python. Это, кажется, полная, но незакрепленная структура, использующая docstrings.
  • http://www.nongnu.org/pydbc/ - PyDBC, который реализует контракты с использованием метаклассов. Также не поддерживается в течение нескольких лет.

Мои вопросы: вы использовали DBC с Python для зрелого кода производства? Насколько хорошо это работало/стоило ли это усилий? Какие инструменты вы бы порекомендовали?

4b9b3361

Ответ 1

Найденный вами PEP еще не принят, поэтому нет стандартного или принятого способа сделать это (но вы всегда можете реализовать PEP самостоятельно!). Однако есть несколько разных подходов, которые вы нашли.

Возможно, самый легкий вес - это просто использовать декораторы Python. Там набор декораторов для предварительных/пост-условий в Python Decorator Library, которые достаточно просты в использовании. Вот пример с этой страницы:

  >>> def in_ge20(inval):
  ...    assert inval >= 20, 'Input value < 20'
  ...
  >>> def out_lt30(retval, inval):
  ...    assert retval < 30, 'Return value >= 30'
  ...
  >>> @precondition(in_ge20)
  ... @postcondition(out_lt30)
  ... def inc(value):
  ...   return value + 1
  ...
  >>> inc(5)
  Traceback (most recent call last):
    ...
  AssertionError: Input value < 20

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

Ответ 2

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

class Math:
    def square_root(self, number)
        """
        Calculate the square-root of C{number}

        @precondition: C{number >= 0}

        @postcondition: C{abs(result * result - number) < 0.01}
        """
        assert number >= 0
        result = self._square_root(number)
        assert abs(result * result - number) < 0.01
        return result

    def _square_root(self, number):
        """
        Abstract method for implementing L{square_root()}
        """
        raise NotImplementedError()

Я получил квадратный корень в качестве общего примера конструирования по контракту из эпизода о конструировании по контракту на радиотехнике по программному обеспечению (http://www.se-radio.net/2007/03/episode-51-design-by-contract/). Они также упомянули о необходимости языковой поддержки, потому что утверждения не помогли в обеспечении принципа замещения Лискова, хотя мой пример выше направлен на демонстрацию в противном случае. Я также должен упомянуть идиому С++ pimpl (private implementation) как источник вдохновения, хотя это имеет совершенно другую цель.

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

Лучшая поддержка инструмента может предложить нам еще большую мощность в будущем, я приветствую это.

Ответ 3

Я не использовал дизайн по контракту в Python, поэтому я не могу ответить на все ваши вопросы. Однако я потратил некоторое время на просмотр библиотеки контрактов, последняя версия которой была выпущена недавно, и она выглядит довольно красиво.

В reddit было обсуждение этой библиотеки.

Ответ 4

Хотя это не совсем проектирование по контракту, некоторые платформы тестирования предпочитают подход к тестированию свойств концептуально очень близок.

Рандомизированное тестирование на наличие определенных свойств во время выполнения позволяет легко проверить:

  • инварианты
  • домены входных и выходных значений
  • другие pre- и постусловия

Для Python есть несколько сред тестирования в стиле QuickCheck:

Ответ 5

Мы хотели использовать предварительные/постусловия/инварианты в нашем производственном коде, но обнаружили, что во всех современных библиотеках разработки по контракту отсутствуют информационные сообщения и надлежащее наследование.

Поэтому мы разработали icontract. Сообщения об ошибках автоматически генерируются путем повторного обхода декомпилированного кода функции и оценки всех задействованных значений:

import icontract

>>> class B:
...     def __init__(self) -> None:
...         self.x = 7
...
...     def y(self) -> int:
...         return 2
...
...     def __repr__(self) -> str:
...         return "instance of B"
...
>>> class A:
...     def __init__(self)->None:
...         self.b = B()
...
...     def __repr__(self) -> str:
...         return "instance of A"
...
>>> SOME_GLOBAL_VAR = 13
>>> @icontract.pre(lambda a: a.b.x + a.b.y() > SOME_GLOBAL_VAR)
... def some_func(a: A) -> None:
...     pass
...
>>> an_a = A()
>>> some_func(an_a)
Traceback (most recent call last):
  ...
icontract.ViolationError: 
Precondition violated: (a.b.x + a.b.y()) > SOME_GLOBAL_VAR:
SOME_GLOBAL_VAR was 13
a was instance of A
a.b was instance of B
a.b.x was 7
a.b.y() was 2

Мы нашли библиотеку довольно полезной как в процессе производства (из-за информационных сообщений), так и во время разработки (поскольку она позволяет выявлять ошибки на ранних этапах).