Hacker News

Python tipa pārbaudītāja salīdzinājums: tukša konteinera secinājumi

komentāri

15 min read Via pyrefly.org

Mewayz Team

Editorial Team

Hacker News

Kāpēc tukši konteineri sabojā Python tipa pārbaudītājus — un ko jūs varat darīt ar to

Python pakāpeniskās mašīnrakstīšanas sistēma ir ievērojami attīstījusies, kopš 2015. gadā PEP 484 ieviesa tipveida mājienus. Mūsdienās miljoniem izstrādātāju paļaujas uz statiskā tipa pārbaudītāju, lai novērstu kļūdas, pirms tās nonāk ražošanā. Bet ir smalks, nomākts tipa sistēmas stūris, kas joprojām pakludina pat pieredzējušus inženierus: kāda veida ir tukša tvertne? Ja ierakstāt x = [] bez anotācijas, veida pārbaudītājam ir jāuzmin, un dažādi pārbaudītāji uzmin atšķirīgi. Šī atšķirība rada reālas problēmas komandām, kuras uztur lielas kodu bāzes, kur, pārslēdzot vai apvienojot tipa pārbaudītājus, vienas nakts laikā var rasties simtiem negaidītu kļūdu.

Šajā rakstā ir aprakstīts, kā četras galvenās Python tipa pārbaudītājas — mypy, pyright, pytype un pyre — apstrādā tukša konteinera secinājumus, kāpēc tie nepiekrīt un kādas praktiskas stratēģijas varat izmantot, lai rakstītu tipam drošu Python neatkarīgi no jūsu rīku izvēles.

Pamatproblēma: tukši konteineri pēc būtības ir neskaidri

Apsveriet šo nekaitīgo Python rindu: results = []. Vai rezultāti ir saraksts[int]? saraksts[str]? saraksts[dict[str, Any]]? Bez papildu konteksta to īsti nevar zināt. Python izpildlaikam ir vienalga — saraksti pēc būtības ir neviendabīgi, taču statiskā tipa pārbaudītājiem katram mainīgajam ir jāpiešķir konkrēts tips, lai veiktu savu darbu. Tas rada būtisku spriedzi starp Python dinamisko elastību un garantijām, ko mēģina nodrošināt statiskā analīze.

Problēmu savienojumi ar vārdnīcām un komplektiem. Tukšs {} faktiski tiek parsēts kā dikts, nevis kā kopa, kas papildus tipa līmeņa neskaidrībai pievieno sintaktisko neskaidrību. Un ligzdotie konteineri — domājiet par defaultdict(list) vai results = {k: [] for k in keys — nospiež secinājumus līdz galam. Katram tipa pārbaudītājam ir izstrādāta sava heiristika, un atšķirības ir daudz būtiskākas, nekā vairums izstrādātāju saprot.

Ražošanas sistēmās, kas apstrādā reālas darba slodzes — neatkarīgi no tā, vai tas ir CRM, kas apstrādā klientu ierakstus, rēķinu izrakstīšanas modulis, kas ģenerē rindas vienības, vai analītikas konveijers, kas apkopo metriku, tukši konteineri pastāvīgi parādās kā inicializācijas modeļi. Kļūdaini to tipi rada ne tikai brīdinājumus par līkumu; tas var maskēt oriģinālas kļūdas, kas tiek izlaistas izpildlaikā.

Mypy: Atliktais secinājums ar netiešu jebkuru

Mypy, vecākais un visplašāk izmantotais Python tipa pārbaudītājs, izmanto salīdzinoši saudzīgu pieeju tukšiem konteineriem. Kad tas funkcijas tvērumā sastopas ar x = [], tas mēģina atlikt veida lēmumu un secināt elementa veidu no turpmākās lietošanas. Ja ierakstāt x = [], kam seko x.append(42), mypy izsecinās list[int]. Šī “pievienošanās” stratēģija pārsteidzoši labi darbojas vienkāršos gadījumos, kad konteiners ir aizpildīts tajā pašā tvērumā.

Tomēr mypy uzvedība krasi mainās atkarībā no konteksta un stingrības iestatījumiem. Moduļa tvērumā (augšējā līmeņa kods) vai tad, kad konteiners pirms aizpildīšanas tiek nodots citai funkcijai, mypy bieži atgriežas pie saraksta[Jebkurš]. Zem karoga --strict tas aktivizē kļūdu, taču noklusējuma režīmā tā tiek palaista klusi. Tas nozīmē, ka komandas, kas izmanto mypy bez stingrā režīma, var uzkrāt desmitiem netieši ievadītu konteineru, kas darbojas kā evakuācijas lūkas no tipa sistēmas, tādējādi pārkāpjot tās mērķi.

Īpaši smalka darbība: mypy versijas, kas vecākas par 0,990, dažkārt iekšēji izsecina saraksts[Nezināms] un pēc tam uzdevuma laikā tiek paplašināts līdz saraksts[Jebkurš]. Pēc 0.990 secinājumi tika padarīti stingrāki, taču izmaiņas izjauca pārsteidzoši daudz reālās pasaules kodu bāzu, kas bija paļāvušās uz pieļaujamo uzvedību, to neapzinoties. Šī ir periodiska tēma — tukša konteinera secinājuma izmaiņas ir viens no visvairāk traucējošajiem tipa pārbaudītāja atjauninājumiem, jo modeļi ir tik visuresoši.

Pirtiesības: stingrs secinājums un "Nezināmais" veids

Pyright, ko izstrādājusi Microsoft un nodrošina Pylance VS Code, ieņem principiāli atšķirīgu filozofisku nostāju. Tā vietā, lai klusībā atgrieztos pie jebkura, autortiesības nošķir Nezināms (veids, kas vēl nav noteikts) un Jebkurš (precīza atteikšanās no tipa pārbaudes). Rakstot x = [] autortiesību stingrajā režīmā, tiek secināts saraksts[Nezināms] un tiek ziņots par diagnostiku, liekot jums sniegt anotāciju.

Pirights ir arī agresīvāks attiecībā uz sašaurināšanu darbības jomā. Ja rakstat:

  • x = [], kam seko x.append("sveiki") — autortiesības secina, ka saraksts[str]
  • x = [], kam seko x.append(1), pēc tam x.append("sveiki") — autortiesības secina, ka saraksts[int | str]
  • x = [] ir tieši nodots funkcijai, kas sagaida list[int] — autortiesības secina list[int] no zvana vietnes konteksta
  • x = [] atgriezts no funkcijas bez atgriešanas veida anotācijas — autortiesības ziņo par kļūdu, nevis minē

Šis divvirzienu secinājums (izmantojot gan turpmāko lietojumu, gan sagaidāmos veidus no zvanu vietnēm) padara autortiesības ievērojami precīzākus nekā mypy tukšiem konteineriem. Kompromiss ir daudzvārdība: saskaņā ar analīzi no vairākiem atvērtā pirmkoda migrācijas pārskatiem, izmantojot autortiesību stingro režīmu, tiek atzīmēts aptuveni 30–40% vairāk problēmu parastā bezanotētā kodu bāzē, salīdzinot ar mypy stingro režīmu. Komandām, kas veido sarežģītas aizmugursistēmas — piemēram, platformu, kas pārvalda 207 savstarpēji savienotus moduļus, kas aptver CRM, algu sarakstu un analīzi, autortiesību stingrība uztver smalkas saskarnes neatbilstības, kuras saudzīgs secinājums varētu palaist garām.

Pytype un Pyre: Mazāk ceļotie ceļi

Google pytype, iespējams, izmanto vispragmatiskāko pieeju. Tā vietā, lai pieprasītu anotācijas vai atgrieztos pie jebkura, pytype izmanto visas programmas analīzi, lai izsekotu, kā konteiners tiek izmantots pāri funkciju robežām. Ja vienā funkcijā izveidojat tukšu sarakstu un nododat to citai, kas pievieno veselus skaitļus, pytype bieži var izsecināt list[int] bez anotācijām. Šis starpfunkciju secinājums ir skaitļošanas ziņā dārgs — pytype ir ievērojami lēnāks nekā mypy vai pyright lielās kodu bāzēs, taču tas rada mazāk kļūdaini pozitīvu kodu bez anotācijas.

Pytype ievieš arī jēdzienu "daļēji veidi" tukšiem konteineriem. Tikko izveidots [] iegūst daļēju veidu, kas tiek pakāpeniski pilnveidots, kad pārbaudītājs saskaras ar lielāku lietojumu. Tas ir konceptuāli elegants, taču var radīt mulsinošus kļūdu ziņojumus, ja daļēju veidu nevar pilnībā atrisināt, piemēram, ja tukšs konteiners plūst cauri vairākām funkcijām, un tas nekad netiek aizpildīts.

💡 DID YOU KNOW?

Mewayz replaces 8+ business tools in one platform

CRM · Invoicing · HR · Projects · Booking · eCommerce · POS · Analytics. Free forever plan available.

Start Free →

Tikmēr Meta pire tuvinās mypy uzvedībai, taču ar stingrākiem noklusējuma iestatījumiem. Pyre uzskata x = []sarakstu[nezināms], un lielākajā daļā kontekstu ir nepieciešama anotācija. Pyre atšķiras ar to, ka tiek apstrādāti tukši vārdnīcas burti, kas tiek izmantoti kā kwargs — tas ir izplatīts tīmekļa ietvaru modelis. Pyre ir īpaša gadījuma loģika, lai secinātu vārdnīcu veidus no atslēgvārdu argumentu kontekstiem, tādējādi samazinot anotāciju slogu ietvaru bāzēs. Tā kā lielākajā daļā mūsdienu tīmekļa lietojumprogrammu konfigurācijai un pieprasījumu apstrādei tiek plaši izmantota vārdnīcas izpakošana, šis pragmatisms atmaksājas.

Ietekme uz reālo pasauli: kad rodas secinājumi par atšķirībām

Atšķirības starp tipu pārbaudītājiem var šķist akadēmiskas, kamēr tās nepārbaudīsit ražošanas kodu bāzē. Apsveriet biznesa lietojumprogrammās izplatītu modeli: datu struktūras inicializācija, kas tiek aizpildīta ar nosacījumu.

Visbīstamākie tukšie konteineri nav tie, kas ir atzīmēti ar pārbaudītāja karodziņiem — tie ir tie, kas klusi pāriet ar izsecinātu jebkuru veidu, ļaujot bez brīdinājuma uzkrāties nesaderīgiem datiem, līdz pakārtotā funkcija izpildes laikā avarē ar tipa kļūdu, kuras izcelsmi ir gandrīz neiespējami izsekot.

Konkrēts piemērs: fintech startup komanda ziņoja, ka ir pavadījusi trīs dienas, atkļūdojot ražošanas problēmu, kur tukšu sarakstu, kas inicializēts maksājumu apstrādes funkcijā, mypy secināja kā saraksts[Jebkurš]. Sarakstā bija jāiekļauj Decimāldaļas objekti valūtas summām, taču koda ceļā tika pievienotas peldošās vērtības. Mypy maigais secinājums klusībā to atļāva. Kļūda parādījās tikai tad, kad noapaļošanas kļūdas peldošajā aritmētikā izraisīja 0,01 ASV dolāra neatbilstību 12 000 rēķinu partijai. Ja viņi būtu izmantojuši autortiesības stingrā režīmā vai vienkārši anotējuši tukšo sarakstu kā saraksts[Decimāldaļa], kļūda būtu konstatēta izstrādes laikā.

Uzņēmumā Mewayz, kur platforma apstrādā rēķinu izrakstīšanu, algu aprēķinus un finanšu analīzi vairāk nekā 138 000 lietotāju kontos, šāda veida drošības atšķirības nav teorētiskas — tā ir atšķirība starp pareizu algu sarakstu un dārgiem pārrēķiniem. Stingra drukāšanas disciplīna saistībā ar konteinera inicializēšanu ir viena no tām "garlaicīgām" inženierijas praksēm, kas novērš aizraujošus ražošanas incidentus.

Aizsardzības konteineru inicializēšanas paraugprakse

Neatkarīgi no tā, kāda veida pārbaudītāju jūsu komanda izmanto, ir konkrētas stratēģijas, lai pilnībā novērstu tukšu konteineru neskaidrības. Mērķis ir nekad nepaļauties uz secinājumiem par tukšiem konteineriem — norādiet veidu, lai jūsu kods būtu pārnēsājams visās pārbaudītājos un imūna pret secinājumu uzvedības izmaiņām dažādās versijās.

  1. Vienmēr anotēt tukšos konteinera mainīgos. Ierakstiet results: list[int] = [], nevis results = []. Nelielās detalizētības izmaksas ir niecīgas salīdzinājumā ar ietaupīto laiku atkļūdošanai. Šī vienīgā prakse novērš aptuveni 80% tukša konteinera secinājumu problēmu.
  2. Sarežģītiem konteineriem izmantojiet rūpnīcas funkcijas. cache = {} vietā ierakstiet funkciju, piemēram, def make_cache() -> dict[str, list[UserRecord]]: return {}. Atgriešanas veida anotācija padara paredzēto veidu nepārprotamu un pašdokumentējošu.
  3. Netriviāliem tipiem dodiet priekšroku drukātiem konstruktoriem, nevis literāļiem. Ierakstiet vienumus: set[int] = set(), nevis paļaujieties uz kopas izpratnes secinājumiem. Parametriem noklusējuma diktāts un skaitītājs vienmēr norādiet tipa parametru: counts: Counter[str] = Counter().
  4. Konfigurējiet sava tipa pārbaudītāja stingro režīmu jaunam kodam. Gan mypy, gan pyright atbalsta katra faila vai direktorija konfigurāciju. Iespējojiet stingru jauno moduļu pārbaudi, vienlaikus pakāpeniski migrējot mantoto kodu. Tas novērš jaunu netieši ievadītu konteineru uzkrāšanos.
  5. Pievienojiet tipa pārbaudītāja salīdzinājumu savam CI konveijeram. Palaižot gan mypy, gan pyright savā kodu bāzē, agrīni tiek konstatētas secinājumu atšķirības. Ja modelis iztur vienu pārbaudītāju, bet neiztur citu, tas ir signāls, ka veids nav pietiekami precīzs.

Lielāks attēls: tipa pārbaude kā komandas prakse

Tukša konteinera secinājums galu galā ir mikrokosmoss lielākam izaicinājumam Python tipa sistēmā: spriedze starp ērtībām un drošību. Python filozofija “mēs visi piekrītam pieaugušajiem” lieliski darbojas prototipiem un skriptiem, taču ražošanas sistēmām, kas apkalpo tūkstošiem lietotāju, ir vajadzīgas stingrākas garantijas. Fakts, ka četrām galvenajām tipu pārbaudītājām ir domstarpības par kaut ko tik būtisku kā [] veids, liecina, ka Python rakstīšanas ekosistēma joprojām attīstās.

Inženieru komandām, kas veido sarežģītas platformas — neatkarīgi no tā, vai pārvaldāt dažus mikropakalpojumus vai integrētu sistēmu ar simtiem savstarpēji savienotu moduļu, piemēram, Mewayz biznesa operētājsistēmu, praktiskais padoms ir vienkāršs: nepaļaujieties uz secinājumiem par tukšiem konteineriem, izvēlieties tipa pārbaudītāju un stingri konfigurējiet to, un tipu anotācijas uzskatiet par mašīnu pārbaudāmu dokumentāciju. Piecas minūtes, kas pavadītas, rakstot sarakstu[rēķins], nevis [], ietaupīsit vairākas stundas, kad tiks veikta atkļūdošana, kad jūsu kodu bāze tiks mērogota.

Tā kā PEP 696 (noklusējuma tipa parametri) un PEP 695 (tipa parametru sintakse) turpina nonākt jaunākās Python versijās, precīzas rakstīšanas ergonomika turpinās uzlaboties. Atšķirība starp "anotētajiem" un "benotizētajiem" Python samazināsies. Taču līdz šai dienai nepārprotami konteineru veidi joprojām ir viens no augstākajiem IA praksēm Python izstrādātāja rīkkopā — tā ir neliela disciplīna, kas maksā saliktos procentus par katru moduli, katru sprintu un katru ražošanas izvietošanu.

Izveidojiet sava uzņēmuma OS jau šodien

No ārštata darbiniekiem līdz aģentūrām, Mewayz nodrošina vairāk nekā 138 000 uzņēmumu ar 207 integrētiem moduļiem. Sāciet bez maksas, jauniniet, kad izaugsit.

Izveidot bezmaksas kontu →

Bieži uzdotie jautājumi

Kāpēc rakstīšanas pārbaudītāji nevar vienoties par tukšā saraksta veidu?

Kad rakstāt “x = []”, tipa pārbaudītājam ir jāizsecina tips bez skaidriem padomiem. Dažādi pārbaudītāji izmanto dažādas stratēģijas: daži secina "saraksts[Jebkurš]" (jebkura saraksts), savukārt citi var secināt par konkrētāku, bet nepareizu veidu, piemēram, "saraksts[Nav]". Šis universālā standarta trūkums ir iemesls, kāpēc viņi nepiekrīt. Projektiem, kuros izmanto vairākus pārbaudītājus, šī nekonsekvence var radīt nopietnas galvassāpes, izjaucot analīzi vienā rīkā, bet izturot citā.

Kāds ir vienkāršākais veids, kā novērst tukša konteinera kļūdas?

Vienkāršākais risinājums ir nodrošināt precīza veida anotāciju. `my_list = []` vietā ierakstiet `my_list: list[str] = []`, lai skaidri deklarētu paredzēto veidu. Tas novērš jebkādas neskaidrības tipa pārbaudītājam, nodrošinot konsekventu darbību dažādos rīkos, piemēram, mypy, Pyright un Pyre. Šī prakse ir ieteicama visām tukšo konteineru inicializācijas reizēm, lai novērstu secinājumu kļūdas.

Kā rīkoties ar tukšiem konteineriem klases definīcijās?

Šī ir izplatīta problēma, jo klasēs ietvertajām anotācijām ir nepieciešama īpaša apstrāde. Ja saraksts ir paredzēts kā klases atribūts, ir jāizmanto importa anotācijas no __nākotnes__ importa anotācijām vai anotācija ClassVar. Piemēram, `klase MyClass: mans_saraksts: ClassVar[saraksts[str]] = []`. Bez tā tipa pārbaudītājam var rasties grūtības pareizi secināt veidu, kā rezultātā rodas kļūdas.

Vai ir rīki, kas palīdz pārvaldīt šīs rakstīšanas problēmas lielos projektos?

Jā, uzlabotas tipa pārbaudītāji, piemēram, Pyright (kas nodrošina Pylance VS kodā), ir īpaši labi, lai apstrādātu sarežģītus secinājumus. Lielām kodu bāzēm tādas platformas kā Mewayz (piedāvā 207 analīzes moduļus par 19 ASV dolāriem mēnesī) var nodrošināt dziļāku, konsekventāku tipa pārbaudi un palīdzēt ieviest anotācijas praksi visā jūsu komandā, mazinot rakstā aplūkotās neatbilstības.

Try Mewayz Free

All-in-one platform for CRM, invoicing, projects, HR & more. No credit card required.

Start managing your business smarter today

Join 30,000+ businesses. Free forever plan · No credit card required.

Ready to put this into practice?

Join 30,000+ businesses using Mewayz. Free forever plan — no credit card required.

Start Free Trial →

Ready to take action?

Start your free Mewayz trial today

All-in-one business platform. No credit card required.

Start Free →

14-day free trial · No credit card · Cancel anytime