Hacker News

Porównanie sprawdzania typów Pythona: wnioskowanie o pustym kontenerze

Porównaj, jak mypy, pyright i inne moduły sprawdzające typy Python radzą sobie z wnioskowaniem o pustym kontenerze. Poznaj praktyczne poprawki dotyczące stopniowego pisania przypadków brzegowych w dużych bazach kodu.

5 min. przeczytaj

Mewayz Team

Editorial Team

Hacker News

Dlaczego puste kontenery psują sprawdzanie typów Pythona — i co można z tym zrobić

System stopniowego pisania w Pythonie znacznie się rozwinął, odkąd w PEP 484 wprowadzono wskazówki dotyczące typów w 2015 roku. Obecnie miliony programistów polega na statycznych modułach sprawdzania typów, aby wychwytywać błędy, zanim trafią one do środowiska produkcyjnego. Istnieje jednak subtelny, frustrujący element systemu typów, który wciąż zaskakuje nawet doświadczonych inżynierów: jaki typ ma pusty pojemnik? Kiedy napiszesz x = [] bez adnotacji, Twój moduł sprawdzający typ musi zgadywać — a różne moduły sprawdzające zgadują inaczej. Ta rozbieżność stwarza prawdziwe problemy dla zespołów utrzymujących duże bazy kodów, gdzie przełączanie lub łączenie modułów sprawdzania typów może z dnia na dzień ujawnić setki nieoczekiwanych błędów.

W tym artykule opisano, w jaki sposób cztery główne moduły sprawdzania typów w języku Python — mypy, pyright, pytype i pyre — radzą sobie z wnioskowaniem o pustych kontenerach, dlaczego się nie zgadzają i jakie praktyczne strategie można zastosować, aby pisać w języku Python z bezpiecznym typem, niezależnie od wybranego narzędzia.

Podstawowy problem: puste pojemniki są z natury niejednoznaczne

Rozważmy tę nieszkodliwą linię Pythona:results = []. Czy wyniki są listą [int]? Lista[str]? Lista[dict[str, Any]]? Bez dodatkowego kontekstu naprawdę nie ma sposobu, aby się tego dowiedzieć. Środowisko wykonawcze Pythona nie przejmuje się tym — listy są z natury heterogeniczne — ale statyczne moduły sprawdzania typów muszą przypisać konkretny typ do każdej zmiennej, aby wykonać swoje zadanie. Stwarza to zasadnicze napięcie pomiędzy dynamiczną elastycznością Pythona a gwarancjami, jakie stara się zapewnić analiza statyczna.

Problem dotyczy słowników i zestawów. Pusty {} jest w rzeczywistości analizowany jako słownik, a nie zestaw, co dodaje niejednoznaczność składniową oprócz niejednoznaczności na poziomie typu. A zagnieżdżone kontenery — pomyśl defaultdict(list) lubresults = {k: [] for k in keys} — wypychają silniki wnioskowania do granic ich możliwości. Każdy moduł sprawdzania typów opracował własną heurystykę, a różnice są bardziej znaczące, niż zdaje sobie sprawę większość programistów.

W systemach produkcyjnych przetwarzających rzeczywiste obciążenia — niezależnie od tego, czy jest to CRM obsługujący dokumentację klientów, moduł fakturowania generujący pozycje pojedyncze, czy potok analityczny agregujący metryki — puste kontenery pojawiają się stale jako wzorce inicjalizacji. Błędne określenie ich typów powoduje nie tylko wygenerowanie ostrzeżeń o linterze; może maskować prawdziwe błędy, które przedostają się do środowiska wykonawczego.

💡 CZY WIESZ?

Mewayz replaces 8+ business tools in one platform

CRM · Fakturowanie · HR · Projekty · Rezerwacje · eCommerce · POS · Analityka. Darmowy plan dostępny na zawsze.

Zacznij za darmo →

Mypy: Odroczone wnioskowanie z ukrytym dowolnym

Mypy, najstarszy i najszerzej stosowany moduł sprawdzania typu Pythona, stosunkowo łagodnie podchodzi do pustych kontenerów. Kiedy w zakresie funkcji napotka x = [], próbuje odłożyć decyzję o typie i wywnioskować typ elementu na podstawie późniejszego użycia. Jeśli napiszesz x = [], a następnie x.append(42), mypy wywnioskowie listę[int]. Ta strategia „łączenia” działa zaskakująco dobrze w prostych przypadkach, gdy kontener jest zapełniony w tym samym zakresie.

Jednak zachowanie mypy zmienia się dramatycznie w zależności od kontekstu i ustawień ścisłości. W zakresie modułu (kod najwyższego poziomu) lub gdy kontener jest przekazywany do innej funkcji przed zapełnieniem, mypy często powraca do list[Any]. Pod flagą --strict powoduje to błąd, ale w trybie domyślnym po cichu przechodzi. Oznacza to, że zespoły uruchamiające mypy bez trybu ścisłego mogą gromadzić dziesiątki kontenerów z niejawnym typem, które działają jak luki ratunkowe z systemu typów, co jest sprzeczne z jego celem.

Jedno szczególnie subtelne zachowanie: wersje mypy starsze niż 0.990 czasami wnioskowały wewnętrznie o liście [Nieznane], a następnie rozszerzały się do listy [Any] po przypisaniu. Po wersji 0.990 wnioskowanie zostało zaostrzone, ale zmiana złamała zaskakującą liczbę baz kodu w świecie rzeczywistym, które polegały na permisywnym zachowaniu, nie zdając sobie z tego sprawy. Jest to powracający temat — zmiany w wnioskowaniu o pustych kontenerach należą do najbardziej uciążliwych aktualizacji modułu sprawdzania typów, ponieważ wzorce są wszechobecne.

Pyright: ścisłe wnioskowanie i typ „nieznany”.

Pyright, opracowany przez Microsoft i stanowiący podstawę Pylance w VS Code, przyjmuje zasadniczo odmienne stanowisko filozoficzne. Zamiast po cichu

Build Your Business OS Today

From freelancers to agencies, Mewayz powers 138,000+ businesses with 207 integrated modules. Start free, upgrade when you grow.

Create Free Account →

Wypróbuj Mewayz za Darmo

Kompleksowa platforma dla CRM, fakturowania, projektów, HR i więcej. Karta kredytowa nie jest wymagana.

Zacznij dziś zarządzać swoją firmą mądrzej.

Dołącz do 30,000+ firm. Plan darmowy na zawsze · Bez karty kredytowej.

Uznałeś to za przydatne? Udostępnij to.

Gotowy, aby wprowadzić to w życie?

Dołącz do 30,000+ firm korzystających z Mewayz. Darmowy plan forever — karta kredytowa nie jest wymagana.

Rozpocznij darmowy okres próbny →

Gotowy, by podjąć działanie?

Rozpocznij swój darmowy okres próbny Mewayz dziś

Platforma biznesowa wszystko w jednym. Karta kredytowa nie jest wymagana.

Zacznij za darmo →

14-dniowy darmowy okres próbny · Bez karty kredytowej · Anuluj w dowolnym momencie