Rruga për të kontrolluar 4 milion rreshta të kodit Python. Pjesa 1

Sot sjellim në vëmendjen tuaj pjesën e parë të përkthimit të materialit se si Dropbox merret me kontrollin e tipit të kodit Python.

Rruga për të kontrolluar 4 milion rreshta të kodit Python. Pjesa 1

Dropbox shkruan shumë në Python. Është një gjuhë që ne e përdorim jashtëzakonisht gjerësisht, si për shërbimet e fundit ashtu edhe për aplikacionet e klientëve desktop. Ne përdorim gjithashtu shumë Go, TypeScript dhe Rust, por Python është gjuha jonë kryesore. Duke marrë parasysh shkallën tonë, dhe ne po flasim për miliona rreshta të kodit Python, doli që shtypja dinamike e një kodi të tillë ndërlikoi në mënyrë të panevojshme kuptimin e tij dhe filloi të ndikojë seriozisht në produktivitetin e punës. Për të zbutur këtë problem, ne kemi filluar të kalojmë gradualisht kodin tonë në kontrollimin e tipit statik duke përdorur mypy. Ky është ndoshta sistemi më i popullarizuar i kontrollit të tipit të pavarur për Python. Mypy është një projekt me burim të hapur, zhvilluesit kryesorë të tij punojnë në Dropbox.

Dropbox ishte një nga kompanitë e para që zbatoi kontrollin statik të tipit në kodin Python në këtë shkallë. Mypy përdoret në mijëra projekte këto ditë. Ky mjet i panumërt herë, siç thonë ata, "testuar në betejë". Kemi bërë një rrugë të gjatë për të arritur këtu ku jemi tani. Gjatë rrugës, pati shumë ndërmarrje të pasuksesshme dhe eksperimente të dështuara. Ky postim mbulon historinë e kontrollit të tipit statik në Python, që nga fillimet e tij të vështira si pjesë e projektit tim kërkimor, deri në ditët e sotme, kur kontrollimi i tipit dhe aludimi i tipit janë bërë të zakonshme për zhvillues të panumërt që shkruajnë në Python. Këta mekanizma tani mbështeten nga shumë mjete si IDE dhe analizues kodesh.

Lexoni pjesën e dytë

Pse është i nevojshëm kontrolli i tipit?

Nëse keni përdorur ndonjëherë Python të shtypur në mënyrë dinamike, mund të keni një farë konfuzioni se pse ka pasur një bujë të tillë rreth shtypjes statike dhe mypy kohët e fundit. Ose ndoshta ju pëlqen Python pikërisht për shkak të shtypjes së tij dinamike dhe ajo që po ndodh thjesht ju shqetëson. Çelësi i vlerës së shtypjes statike është shkalla e zgjidhjeve: sa më i madh të jetë projekti juaj, aq më shumë anoni drejt shtypjes statike dhe në fund, aq më shumë keni nevojë për të.

Supozoni se një projekt i caktuar ka arritur madhësinë e dhjetëra mijëra rreshtave, dhe doli që disa programues po punojnë për të. Duke parë një projekt të ngjashëm, bazuar në përvojën tonë, mund të themi se kuptimi i kodit të tij do të jetë çelësi për t'i mbajtur zhvilluesit produktivë. Pa shënime të tipit, mund të jetë e vështirë të kuptosh, për shembull, çfarë argumentesh t'i kalosh një funksioni ose çfarë llojesh mund të kthejë një funksion. Këtu janë pyetjet tipike që shpesh janë të vështira për t'u përgjigjur pa përdorur shënime të tipit:

  • A mund të kthehet ky funksion None?
  • Cili duhet të jetë ky argument? items?
  • Cili është lloji i atributit id: int eshte, str, apo ndoshta ndonjë lloj i personalizuar?
  • A duhet të jetë ky argument një listë? A është e mundur t'i kalosh një tufë?

Nëse shikoni pjesën e mëposhtme të kodit të shënuar me tip dhe përpiqeni t'u përgjigjeni pyetjeve të ngjashme, rezulton se kjo është detyra më e thjeshtë:

class Resource:
    id: bytes
    ...
    def read_metadata(self, 
                      items: Sequence[str]) -> Dict[str, MetadataItem]:
        ...

  • read_metadata nuk kthehet None, pasi lloji i kthimit nuk është Optional[…].
  • Argument items është një sekuencë vijash. Nuk mund të përsëritet rastësisht.
  • Atribut id është një varg bajtësh.

Në një botë ideale, do të pritej që të gjitha hollësitë e tilla të përshkruheshin në dokumentacionin e integruar (docstring). Por përvoja jep shumë shembuj për faktin se një dokumentacion i tillë shpesh nuk vërehet në kodin me të cilin duhet të punoni. Edhe nëse një dokumentacion i tillë është i pranishëm në kod, nuk mund të mbështetet në korrektësinë e tij absolute. Ky dokumentacion mund të jetë i paqartë, i pasaktë dhe i hapur për keqkuptime. Në ekipe të mëdha ose projekte të mëdha, ky problem mund të bëhet jashtëzakonisht i mprehtë.

Ndërsa Python shkëlqen në fazat e hershme ose të ndërmjetme të projekteve, në një moment projektet dhe kompanitë e suksesshme që përdorin Python mund të përballen me pyetjen jetike: "A duhet të rishkruajmë gjithçka në një gjuhë të shtypur statikisht?".

Sistemet e kontrollit të tipit si mypy zgjidhin problemin e mësipërm duke i siguruar zhvilluesit një gjuhë zyrtare për përshkrimin e llojeve dhe duke kontrolluar që deklaratat e tipit përputhen me zbatimin e programit (dhe, sipas dëshirës, ​​duke kontrolluar ekzistencën e tyre). Në përgjithësi, mund të themi se këto sisteme na vënë në dispozicion diçka si dokumentacion i kontrolluar me kujdes.

Përdorimi i sistemeve të tilla ka avantazhe të tjera, dhe ato tashmë janë plotësisht jo të parëndësishme:

  • Sistemi i kontrollit të tipit mund të zbulojë disa gabime të vogla (dhe jo aq të vogla). Një shembull tipik është kur ata harrojnë të përpunojnë një vlerë None ose ndonjë gjendje tjetër të veçantë.
  • Rifaktorimi i kodit është thjeshtuar shumë sepse sistemi i kontrollit të tipit shpesh është shumë i saktë në lidhje me kodin që duhet ndryshuar. Në të njëjtën kohë, nuk kemi nevojë të shpresojmë për mbulim 100% të kodit me teste, gjë që, në çdo rast, zakonisht nuk është e realizueshme. Nuk kemi nevojë të gërmojmë në thellësi të gjurmës së pirgut për të gjetur shkakun e problemit.
  • Edhe në projekte të mëdha, mypy shpesh mund të bëjë kontroll të plotë të tipit në një fraksion të sekondës. Dhe ekzekutimi i testeve zakonisht zgjat dhjetëra sekonda apo edhe minuta. Sistemi i kontrollit të tipit i jep programuesit reagime të menjëhershme dhe e lejon atë të bëjë punën e tij më shpejt. Ai nuk ka më nevojë të shkruajë teste të brishta dhe të vështira për t'u mbajtur njësi që zëvendësojnë entitetet reale me tallje dhe arna vetëm për të marrë më shpejt rezultatet e testit të kodit.

IDE-të dhe redaktuesit si PyCharm ose Visual Studio Code përdorin fuqinë e shënimeve të tipit për t'u ofruar zhvilluesve plotësimin e kodit, theksimin e gabimeve dhe mbështetjen për konstruktet gjuhësore të përdorura zakonisht. Dhe këto janë vetëm disa nga përfitimet e të shkruarit. Për disa programues, e gjithë kjo është argumenti kryesor në favor të shtypjes. Kjo është diçka që përfiton menjëherë pas zbatimit. Ky rast përdorimi për llojet nuk kërkon një sistem të veçantë të kontrollit të tipit si mypy, megjithëse duhet të theksohet se mypy ndihmon në mbajtjen e shënimeve të tipit në përputhje me kodin.

Sfondi i mypy

Historia e mypy filloi në MB, në Kembrixh, disa vjet para se të bashkohesha me Dropbox. Unë kam qenë i përfshirë, si pjesë e kërkimit tim të doktoraturës, në unifikimin e gjuhëve të shtypura statike dhe dinamike. Jam frymëzuar nga një artikull mbi shtypjen në rritje nga Jeremy Siek dhe Walid Taha, dhe nga projekti Typed Racket. U përpoqa të gjeja mënyra për të përdorur të njëjtën gjuhë programimi për projekte të ndryshme - nga skriptet e vogla deri te bazat e kodit që përbëhen nga shumë miliona rreshta. Në të njëjtën kohë, doja të siguroja që në një projekt të çdo shkalle, nuk do të duhej të bëheshin kompromise shumë të mëdha. Një pjesë e rëndësishme e gjithë kësaj ishte ideja e kalimit gradual nga një projekt prototip i pa tipizuar në një produkt të përfunduar të testuar në mënyrë statike. Në ditët e sotme, këto ide janë marrë kryesisht si të mirëqena, por në vitin 2010 ishte një problem që ishte ende duke u eksploruar në mënyrë aktive.

Puna ime origjinale në kontrollin e tipit nuk kishte për qëllim Python. Në vend të kësaj, përdora një gjuhë të vogël "shtëpiake". Alor. Këtu është një shembull që do t'ju lejojë të kuptoni se për çfarë po flasim (shënimet e tipit janë opsionale këtu):

def Fib(n as Int) as Int
  if n <= 1
    return n
  else
    return Fib(n - 1) + Fib(n - 2)
  end
end

Përdorimi i një gjuhe amtare të thjeshtuar është një qasje e zakonshme që përdoret në kërkimin shkencor. Kjo është kështu, jo vetëm sepse ju lejon të kryeni shpejt eksperimente, dhe gjithashtu për shkak të faktit se ajo që nuk ka të bëjë me studimin mund të injorohet lehtësisht. Gjuhët e programimit të botës reale priren të jenë dukuri në shkallë të gjerë me zbatime komplekse, dhe kjo ngadalëson eksperimentimin. Megjithatë, çdo rezultat i bazuar në një gjuhë të thjeshtuar duket paksa i dyshimtë, pasi në marrjen e këtyre rezultateve studiuesi mund të ketë sakrifikuar konsiderata të rëndësishme për përdorimin praktik të gjuhëve.

Kontrolluesi im i tipit për Alore dukej shumë premtues, por doja ta provoja duke eksperimentuar me kod të vërtetë, i cili, mund të thuash, nuk ishte shkruar në Alore. Për fatin tim, gjuha Alore bazohej kryesisht në të njëjtat ide si Python. Ishte mjaft e lehtë për të ndryshuar tipchecker në mënyrë që të mund të punonte me sintaksën dhe semantikën e Python. Kjo na lejoi të provonim kontrollimin e shtypjes në kodin Python me burim të hapur. Për më tepër, unë shkrova një transpilues për të kthyer kodin e shkruar në Alore në kodin Python dhe e përdora atë për të përkthyer kodin tim të tipit kontrollues. Tani kisha një sistem kontrolli tipi të shkruar në Python që mbështette një nëngrup të Python, një lloj të asaj gjuhe! (Disa vendime arkitekturore që kishin kuptim për Alore ishin të papërshtatshme për Python, dhe kjo është ende e dukshme në pjesë të bazës së kodit mypy.)

Në fakt, gjuha e mbështetur nga sistemi im i tipit nuk mund të quhej plotësisht Python në këtë pikë: ishte një variant i Python për shkak të disa kufizimeve të sintaksës së shënimeve të tipit Python 3.

Dukej si një përzierje e Java dhe Python:

int fib(int n):
    if n <= 1:
        return n
    else:
        return fib(n - 1) + fib(n - 2)

Një nga idetë e mia në atë kohë ishte të përdorja shënime të tipit për të përmirësuar performancën duke përpiluar këtë lloj Python në C, ose ndoshta bajtkod JVM. Unë arrita në fazën e shkrimit të një prototipi përpilues, por e braktisa këtë ide, pasi vetë kontrollimi i tipit dukej mjaft i dobishëm.

Përfundova duke prezantuar projektin tim në PyCon 2013 në Santa Clara. Unë gjithashtu fola për këtë me Guido van Rossum, diktatorin dashamirës të Python-it për jetën. Ai më bindi të heq sintaksën time dhe të qëndroj me sintaksën standarde Python 3. Python 3 mbështet shënimet e funksioneve, kështu që shembulli im mund të rishkruhet siç tregohet më poshtë, duke rezultuar në një program normal Python:

def fib(n: int) -> int:
    if n <= 1:
        return n
    else:
        return fib(n - 1) + fib(n - 2)

Më duhej të bëja disa kompromise (para së gjithash, dua të vërej se kam shpikur sintaksën time pikërisht për këtë arsye). Në veçanti, Python 3.3, versioni më i fundit i gjuhës në atë kohë, nuk mbështeti shënime të ndryshueshme. Diskutova me Guidon me e-mail për mundësi të ndryshme për hartimin sintaksor të shënimeve të tilla. Ne vendosëm të përdorim komentet e tipit për variablat. Kjo i shërbeu qëllimit të synuar, por ishte disi e rëndë (Python 3.6 na dha një sintaksë më të bukur):

products = []  # type: List[str]  # Eww

Komentet e tipit erdhën gjithashtu të dobishëm për të mbështetur Python 2, i cili nuk ka mbështetje të integruar për shënimet e tipit:

f fib(n):
    # type: (int) -> int
    if n <= 1:
        return n
    else:
        return fib(n - 1) + fib(n - 2)

Doli se këto (dhe të tjera) shkëmbime nuk kishin vërtet rëndësi - përfitimet e shtypjes statike nënkuptonin që përdoruesit shpejt harruan sintaksën më pak se perfekte. Meqenëse nuk u përdorën konstruksione të veçanta sintaksore në kodin Python të kontrolluar nga tipi, mjetet ekzistuese të Python dhe proceset e përpunimit të kodit vazhduan të funksionojnë normalisht, duke e bërë shumë më të lehtë për zhvilluesit të mësojnë mjetin e ri.

Guido gjithashtu më bindi të bashkohesha me Dropbox pasi të përfundoja tezën time të diplomimit. Këtu fillon pjesa më interesante e historisë mypy.

Vazhdon…

Të nderuar lexues! Nëse përdorni Python, ju lutemi na tregoni për shkallën e projekteve që zhvilloni në këtë gjuhë.

Rruga për të kontrolluar 4 milion rreshta të kodit Python. Pjesa 1
Rruga për të kontrolluar 4 milion rreshta të kodit Python. Pjesa 1

Burimi: www.habr.com

Shto një koment