O pisaniu kaczek :
Wpisywaniu kaczek pomaga zwykle nie testować typów argumentów w metodach i funkcjach, polegając na dokumentacji, czytelnym kodzie i testowaniu w celu zapewnienia poprawnego użycia.
Informacje na temat sprawdzania poprawności argumentów (EAFP: Łatwiej prosić o wybaczenie niż pozwolenie). Dostosowany przykład stąd :
... uważa się za bardziej pytoniczne:
def my_method(self, key):
try:
value = self.a_dict[member]
except TypeError:
# do something else
Oznacza to, że nikt inny używający twojego kodu nie musi używać prawdziwego słownika lub podklasy - może użyć dowolnego obiektu, który implementuje interfejs mapowania.
Niestety w praktyce nie jest to takie proste. Co jeśli członek w powyższym przykładzie może być liczbą całkowitą? Liczby całkowite są niezmienne - więc używanie ich jako kluczy słownikowych jest całkowicie rozsądne. Są one jednak również używane do indeksowania obiektów typu sekwencji. Jeśli element członkowski jest liczbą całkowitą, wówczas przykład drugi może przepuścić listy i ciągi znaków, a także słowniki.
O programowaniu asertywnym :
Asercje to systematyczny sposób sprawdzania, czy wewnętrzny stan programu jest zgodny z oczekiwaniami programisty, w celu wykrycia błędów. W szczególności są one przydatne do wychwytywania fałszywych założeń poczynionych podczas pisania kodu lub nadużywania interfejsu przez innego programistę. Ponadto mogą do pewnego stopnia działać jako dokumentacja online, czyniąc założenia programisty oczywistymi. („Jawne jest lepsze niż niejawne.”)
Wspomniane pojęcia są czasami w konflikcie, więc liczę na następujące czynniki przy podejmowaniu decyzji, czy w ogóle nie przeprowadzam weryfikacji danych, nie przeprowadzam silnej weryfikacji lub używam stwierdzeń:
Silna walidacja. Przez silną walidację mam na myśli wprowadzenie niestandardowego wyjątku (
ApiError
na przykład). Jeśli moja funkcja / metoda jest częścią publicznego interfejsu API, lepiej zweryfikować argument, aby wyświetlić dobry komunikat o błędzie o nieoczekiwanym typie. Przez sprawdzenie typu nie mam na myśli tylko używaniaisinstance
, ale także tego, czy przekazywany obiekt obsługuje wymagany interfejs (pisanie kaczką). Chociaż dokumentuję interfejs API i określam oczekiwany typ, a użytkownik może nieoczekiwanie skorzystać z mojej funkcji, czuję się bezpieczniej, gdy sprawdzam założenia. Zwykle używamisinstance
i jeśli później chcę obsługiwać inne typy lub kaczki, zmieniam logikę sprawdzania poprawności.Programowanie asertywne. Jeśli mój kod jest nowy, często używam twierdzeń. Jakie są na to twoje porady? Czy później usuwasz twierdzenia z kodu?
Jeśli moja funkcja / metoda nie jest częścią API, ale przekazuje niektóre z jej argumentów do innego kodu, który nie został napisany, przestudiowany lub przetestowany przeze mnie, robię wiele twierdzeń zgodnie z wywoływanym interfejsem. Moja logika za tym - lepiej zawieść w moim kodzie, a następnie gdzieś o 10 poziomów głębiej w stosie śledzenia z niezrozumiałym błędem, który zmusza do częstego debugowania, a następnie i tak dodawania aser do mojego kodu.
Komentarze i porady dotyczące tego, kiedy używać sprawdzania poprawności typu / wartości, czy nie? Przepraszamy za najlepsze sformułowanie pytania.
Rozważmy na przykład następującą funkcję, gdzie Customer
jest model deklaratywny SQLAlchemy:
def add_customer(self, customer):
"""Save new customer into the database.
@param customer: Customer instance, whose id is None
@return: merged into global session customer
"""
# no validation here at all
# let's hope SQLAlchemy session will break if `customer` is not a model instance
customer = self.session.add(customer)
self.session.commit()
return customer
Istnieje kilka sposobów obsługi sprawdzania poprawności:
def add_customer(self, customer):
# this is an API method, so let's validate the input
if not isinstance(customer, Customer):
raise ApiError('Invalid type')
if customer.id is not None:
raise ApiError('id should be None')
customer = self.session.add(customer)
self.session.commit()
return customer
lub
def add_customer(self, customer):
# this is an internal method, but i want to be sure
# that it's a customer model instance
assert isinstance(customer, Customer), 'Achtung!'
assert customer.id is None
customer = self.session.add(customer)
self.session.commit()
return customer
Kiedy i dlaczego miałbyś używać każdego z nich w kontekście pisania kaczek, sprawdzania typów, sprawdzania poprawności danych?
źródło
Odpowiedzi:
Pozwól, że podam kilka zasad przewodnich.
Zasada nr 1. Jak opisano w http://docs.python.org/2/reference/simple_stmts.html narzuty związane z wydajnością asertów można usunąć za pomocą opcji wiersza poleceń, pozostając przy tym do debugowania. Jeśli wydajność stanowi problem, zrób to. Pozostaw twierdzenia. (Ale nie rób nic ważnego w twierdzeniach!)
Zasada nr 2. Jeśli coś twierdzisz i wystąpi błąd krytyczny, użyj potwierdzenia. Robienie czegoś innego nie ma absolutnie żadnej wartości. Jeśli ktoś później chce to zmienić, może zmienić kod lub uniknąć wywołania tej metody.
Zasada nr 3. Nie zakazuj czegoś tylko dlatego, że uważasz, że jest to głupota. Co jeśli twoja metoda pozwala na ciągi znaków? Jeśli to działa, to działa.
Zasada nr 4. Odrzuć rzeczy, które są oznakami prawdopodobnych błędów. Na przykład rozważ przesłanie słownika opcji. Jeśli ten słownik zawiera rzeczy, które nie są prawidłowymi opcjami, oznacza to, że ktoś nie zrozumiał Twojego interfejsu API lub miał literówkę. Wysadzenie w to bardziej prawdopodobne jest złapanie literówki niż powstrzymanie kogoś przed zrobieniem czegoś rozsądnego.
W oparciu o pierwsze 2 zasady twoja druga wersja może zostać wyrzucona. Który z pozostałych dwóch wolisz, to kwestia gustu. Który według Ciebie jest bardziej prawdopodobny? Że ktoś przekaże osobę niebędącą klientem
add_customer
i wszystko się zepsuje (w takim przypadku preferowana jest wersja 3), lub że ktoś w pewnym momencie będzie chciał zastąpić klienta jakimś obiektem proxy, który odpowiada na wszystkie właściwe metody (w takim przypadku preferowana jest wersja 1).Osobiście widziałem oba tryby awarii. Zwykle wybieram wersję 1 z ogólnej zasady, że jestem leniwy i że mniej piszę. (Również tego rodzaju porażka zwykle pojawia się prędzej czy później w dość oczywisty sposób. A kiedy chcę użyć obiektu proxy, denerwuję się bardzo na ludzi, którzy związali mi ręce.) Ale są programiści, których szanuję poszedłby w drugą stronę.
źródło