2011-12-19 15 views
36

Szukam zacząć używać DBC na wielu projektach opartych na Pythonie w pracy i zastanawiam się, jakie doświadczenia mieli z tym inni. Do tej pory moje badania okazało się, co następuje:Korzystanie z Design by Contract w Python

Moje pytania to: czy użyłeś DBC z Pythonem do dojrzałego kodu produkcyjnego? Jak dobrze to działało/czy było warte wysiłku? Jakie narzędzia poleciłbyś?

+0

Należy pamiętać, że można po prostu dziedziczyć z TestCase i obejmować testy jednostkowe w dowolnej klasie. – Marcin

+3

W porządku, ale DBC jest nieco inny, ponieważ uruchamia kontrole produkcji i wszystkich danych wejściowych. Z tego co rozumiem Testy Jednostek są zachowywane w środowisku wykonawczym z predefiniowanym zbiorem danych, podczas gdy DBC jest poziomem powyżej, który zapewnia wszystkie dane wejściowe. W szczególności, myślę, że sensowne jest używanie DBC w moim przypadku, ponieważ znaczna część kodu jest naprawdę ciężka i często musi uzyskać stan z zewnętrznego DB z często zmieniającym się schematem i dość złożonymi relacjami, które są bardzo kłopotliwe w makrofotografii. . – ipartola

+0

Projektowanie za pomocą umowy to miejsce, w którym wyraźnie określa się specyfikację, z którą odpowiada każdy kod. Nie musisz go testować w pełnym czasie pracy. Testy jednostkowe mogą być tą specyfikacją tak samo jak cokolwiek innego. TDD to inny sposób korzystania z testów jednostkowych, w tym przypadku do modelowania oczekiwanego zestawu zachowań. – Marcin

Odpowiedz

4

Nie używałam projektu na podstawie umowy w Pythonie, więc nie mogę odpowiedzieć na wszystkie twoje pytania. Spędziłem jednak trochę czasu, przeglądając bibliotekę contracts, której najnowsza wersja została niedawno wydana i wygląda całkiem nieźle.

Było kilka dyskusji na temat tej biblioteki w reddit.

+1

Ten wygląda ładnie, ale brakuje mu obsługi dużej części DBC: niezmienników klasy. Będę o tym pamiętać. – ipartola

15

PEP, który znalazłeś, nie został jeszcze zaakceptowany, więc nie ma standardowego ani akceptowanego sposobu, aby to zrobić (jednak - zawsze możesz wdrożyć PEP samemu!). Istnieje jednak kilka różnych podejść, jak już zauważyłeś.

Prawdopodobnie najbardziej lekka jest po prostu użycie dekoratorów Python. W zestawie Python Decorator Library znajduje się zestaw dekoratorów do warunków wstępnych/końcowych, które są dość proste w użyciu. Oto przykład z tej strony:

>>> 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 

Teraz wspomnisz o niezmiennikach klas. Są nieco trudniejsze, ale sposób, w jaki bym to robił, to zdefiniowanie opcji sprawdzania niezmiennika, a następnie skorzystanie z funkcji podobnej do dekoratora post-condition, która jest niezmienna na końcu każdego wywołania metody. Jako pierwsze cięcie prawdopodobnie mógłbyś po prostu użyć dekoratora pokwitowań, tak jak jest.

+0

Dziękuję za odpowiedź. Rozumiem sposób, w jaki można to wykorzystać do implementacji DBC w Pythonie. Zastanawiałem się, czy ktoś już osiągnął sukces w którejkolwiek z wymienionych bibliotek, czy w innych bibliotekach. Pytanie brzmi mniej "jak wdrożyć DBC?" i więcej "powinienem zawracać głowę DBC?". – ipartola

+0

@ipartola, ponieważ PEP 316 został "odroczony", nie jest aktywnie opracowywany, ale nie został odrzucony. Tak więc, jeśli chcesz zrobić to dalej, wprowadzenie ulepszeń w PyContract będzie prawdopodobnie dobrym krokiem naprzód. Myślę, że możesz założyć, że praca nad PyContract utknęła na razie. Odpowiedź "czy powinienem przeszkadzać w implementacji DBC" brzmi "tak" w mojej opinii, byłby to bardzo użyteczny dodatek, ale jest to prawdopodobnie subiektywne, ponieważ DBC nie jest jeszcze szeroko stosowane w ekosystemie Pythona. – snim2

+1

Również biblioteka Covenant (https://bitbucket.org/kisielk/covenant/) z linku, który wspomniany @jcollado ma niezmienniki. – snim2

3

Chociaż nie jest to dokładnie projekt według umowy, niektóre ramy testowe sprzyjają testowaniu właściwości są bardzo zbliżone koncepcyjnie.

randomizowane badanie na razie pewne właściwości trzymać w czasie pracy pozwala łatwo sprawdzić:

  • niezmienniki
  • domeny wartości wejściowych i wyjściowych
  • inne przed- i postconditions

dla Pythona istnieje kilka frameworków testowych w stylu QuickCheck:

9

Z mojego doświadczenia design-by-umowy jest warte zrobienia, nawet bez wsparcia językowego.W przypadku metod, które nie są przesłonięte asercje, wraz z docstrings są wystarczające zarówno dla warunków wstępnych i post. W przypadku nadpisywanych metod dzielimy metodę na dwie: metodę publiczną, która sprawdza warunki przed i po, oraz metodę chronioną, która zapewnia implementację, i może być nadpisana przez podklasy. Oto przykład tego ostatniego:

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() 

mam pierwiastkowania jako ogólny przykład wzornictwa-by-umowy z epizodu na design-by-umowy w radiu oprogramowania inżynierskiego (http://www.se-radio.net/2007/03/episode-51-design-by-contract/). Wspomnieli również o potrzebie wsparcia językowego, ponieważ twierdzenia nie były pomocne w zapewnieniu zasady substytucji Liskova, chociaż mój przykład powyżej ma na celu wykazanie, że jest inaczej. Powinienem również wspomnieć o idiomie C++ pimpl (implementacja prywatna) jako źródle inspiracji, choć ma on zupełnie inny cel.

W mojej pracy niedawno refaktoryzowałem ten rodzaj sprawdzania kontraktów w większej hierarchii klas (umowa była już udokumentowana, ale nie została systematycznie przetestowana). Istniejące testy jednostkowe wykazały, że umowy były wielokrotnie naruszane. Mogę tylko dojść do wniosku, że powinno to być zrobione dawno temu, a zakres testów jednostkowych opłaca się jeszcze bardziej, gdy zastosuje się projekt na podstawie umowy. Oczekuję, że każdy, kto spróbuje tej kombinacji technik, wykona te same obserwacje.

Lepsze wsparcie narzędziowe może zaoferować nam jeszcze większą moc w przyszłości, z zadowoleniem przyjmuję to.

+0

Wielka anegdota, która faktycznie odpowiada na pytanie. – doughgle

+0

Popraw mnie, jeśli się mylę, ale to nie wydaje się dobrze z niezmiennikami klasy. Można je uwzględnić w każdym stanie przed i po, ale to zwróci fałszywe alarmy, gdy metoda (1) zrywa invariant (2) wywołuje inną metodę, która sprawdza niezmiennik (3) odtwarza niezmiennik (który jest dozwolony). Możesz przyjąć dyscyplinę polegającą na tym, że nigdy nie wywołujesz publicznych metod, dzwonisz tylko do prywatnych pomocników, ale wtedy warunki wstępne i końcowe nie będą sprawdzane pod kątem tych połączeń. – johncip

+0

Wymaga to również nadpisania metody publicznej, jeśli nadrzędna prywatna implementacja aktualizuje warunki wstępne lub końcowe, ale w praktyce nie jest to wielka sprawa, i nie mam wątpliwości, że inne wspomniane implementacje obsługują te problemy lepiej. – johncip

Powiązane problemy