{"id":38479,"date":"2019-10-31T22:23:56","date_gmt":"2019-10-31T19:23:56","guid":{"rendered":"https:\/\/prohoster.info\/blog\/put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2\/"},"modified":"2019-10-31T22:23:56","modified_gmt":"2019-10-31T19:23:56","slug":"put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2","status":"publish","type":"post","link":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2","title":{"rendered":"Il percorso per il controllo dei tipi di 4 milioni di righe di codice Python. Parte 2","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Oggi pubblichiamo la seconda parte della traduzione del materiale su come Dropbox ha organizzato il controllo dei tipi di diverse milioni di righe di codice Python.<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/468235\/\"><img decoding=\"async\" alt=\"Il percorso per il controllo dei tipi di 4 milioni di righe di codice Python. Parte 2\" src=\"\/wp-content\/uploads\/2019\/09\/3f8f396d724153a0374a3fadca190d55.jpeg\" style=\"display:block;margin: 0 auto;\" \/><\/a><\/noindex><\/p>\n<p>\u2192 <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/468233\/\">Leggi la prima parte<\/a><\/noindex><br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Supporto ufficiale dei tipi (PEP 484)<\/h2>\n<p>\nAbbiamo condotto i primi esperimenti significativi con mypy in Dropbox durante la Hack Week del 2014. La Hack Week \u00e8 un evento organizzato da Dropbox per una settimana. In questo periodo, i dipendenti possono lavorare su qualsiasi cosa! Alcuni dei progetti tecnologici pi\u00f9 famosi di Dropbox sono iniziati proprio in eventi come questo. A seguito di questo esperimento, abbiamo concluso che mypy appare promettente, anche se questo progetto non era ancora pronto per un utilizzo diffuso.<\/p>\n<p>All'epoca circolava l'idea di standardizzare i sistemi di suggerimenti per i tipi Python. Come ho gi\u00e0 detto, a partire da Python 3.0 era possibile utilizzare le annotazioni sui tipi per le funzioni, ma erano solo espressioni arbitrarie, senza una sintassi e semantica definite. Durante l'esecuzione del programma, queste annotazioni venivano per lo pi\u00f9 ignorate. Dopo la Hack Week, abbiamo iniziato a lavorare sulla standardizzazione della semantica. Questo lavoro ha portato alla creazione di <noindex><a rel=\"nofollow\" href=\"https:\/\/www.python.org\/dev\/peps\/pep-0484\/\">PEP 484<\/a><\/noindex> (questo documento \u00e8 stato redatto congiuntamente da Guido van Rossum, \u0141ukasz Langa e me.)<\/p>\n<p>I nostri motivi possono essere visti da due punti di vista. In primo luogo, speravamo che l'intero ecosistema Python potesse adottare un approccio unificato all'uso delle indicazioni sui tipi (type hints - un termine utilizzato in Python come analogo delle 'annotazioni dei tipi'). Considerando i possibili rischi, sarebbe stato meglio rispetto all'uso di numerosi approcci reciprocamente incompatibili. In secondo luogo, volevamo discutere apertamente i meccanismi di annotazione dei tipi con molti membri della comunit\u00e0 Python. In parte, questo desiderio era dettato dal fatto che non volevamo sembrare 'dissidenti' rispetto alle idee di base del linguaggio agli occhi di una vasta scala di programmatori Python. \u00c8 un linguaggio a tipizzazione dinamica, noto per la 'tipizzazione anatra'. All'inizio della comunit\u00e0, non si poteva non avere un certo sospetto nei confronti dell'idea di tipizzazione statica. Ma questa mentalit\u00e0 alla fine si \u00e8 affievolita, dopo che \u00e8 diventato chiaro che la tipizzazione statica non sarebbe stata resa obbligatoria (e dopo che la gente ha compreso che \u00e8 davvero utile).<\/p>\n<p>Alla fine, la sintassi delle indicazioni sui tipi adottata si \u00e8 rivelata molto simile a quella che all'epoca era supportata da mypy. Il documento PEP 484 \u00e8 stato pubblicato insieme a Python 3.5 nel 2015. Python non era pi\u00f9 un linguaggio che supportava solo la tipizzazione dinamica. Mi piace pensare a questo evento come a un importante traguardo nella storia di Python.<\/p>\n<h2>Inizio della migrazione<\/h2>\n<p>\nAlla fine del 2015, in Dropbox, \u00e8 stato creato un team di tre persone per lavorare su mypy. Ne facevano parte Guido van Rossum, Greg Price e David Fisher. Da quel momento, la situazione ha cominciato a evolversi molto rapidamente. Il primo ostacolo alla crescita di mypy \u00e8 stata la prestazione. Come ho accennato prima, in una fase iniziale dello sviluppo del progetto stavo considerando di trasferire l'implementazione di mypy sul linguaggio C, ma questa idea \u00e8 stata per il momento depennata dalla lista. Siamo stati bloccati dall'utilizzo dell'interprete CPython, che non si distingue per la rapidit\u00e0 necessaria per strumenti come mypy. (Il progetto PyPy, un'implementazione alternativa di Python con compilatore JIT, non ci ha aiutato neanche.)<\/p>\n<p>Fortunatamente, ci sono stati di aiuto alcuni miglioramenti algoritmici. Il primo potente \"acceleratore\" \u00e8 stato l'implementazione del controllo incrementale. L'idea di questo miglioramento era semplice: se tutte le dipendenze di un modulo non sono cambiate dall'ultimo avvio di mypy, possiamo utilizzare, durante l'elaborazione delle dipendenze, i dati memorizzati nella cache durante la sessione precedente. Dobbiamo solo effettuare il controllo dei tipi nei file modificati e in quelli che dipendono da essi. Mypy \u00e8 andato anche oltre: se l'interfaccia esterna del modulo non \u00e8 cambiata, mypy ha ritenuto che non fosse necessario ricontrollare gli altri moduli che importano questo modulo.<\/p>\n<p>Il controllo incrementale ci ha aiutato molto nell'annotazione di grandi volumi di codice esistente. Infatti, questo processo di solito comporta molte esecuzioni iterative di mypy, poich\u00e9 le annotazioni vengono aggiunte gradualmente nel codice e migliorate man mano. La prima esecuzione di mypy era ancora molto lenta, poich\u00e9 dovevano essere controllate molte dipendenze. Per migliorare la situazione, abbiamo implementato un meccanismo di caching remoto. Se mypy rileva che la cache locale \u00e8 probabilmente obsoleta, carica l'attuale snapshot della cache per l'intera base di codice da un repository centralizzato. Quindi, esegue, utilizzando questo snapshot, il controllo incrementale. Questo ci ha fatto fare un altro notevole passo avanti nel migliorare le prestazioni di mypy.<\/p>\n<p>\u00c8 stato un periodo di veloce e naturale introduzione del sistema di controllo dei tipi in Dropbox. Entro la fine del 2016, avevamo gi\u00e0 circa 420.000 righe di codice Python con annotazioni sui tipi. Molti utenti hanno accolto con entusiasmo il controllo dei tipi. In Dropbox, mypy \u00e8 stato utilizzato da un numero crescente di squadre di sviluppatori.<\/p>\n<p>Tutto allora sembrava buono, ma ci restava ancora molto da fare. Iniziammo a condurre indagini interne periodiche tra gli utenti per identificare i punti critici del progetto e capire quali questioni dovessero essere risolte per prime (questa pratica \u00e8 ancora utilizzata in azienda ai giorni nostri). Le due questioni pi\u00f9 importanti, come emerse chiaramente, erano due. La prima era necessaria una maggiore copertura del codice con i tipi, la seconda era necessario che mypy funzionasse pi\u00f9 velocemente. Era assolutamente chiaro che il nostro lavoro per accelerare mypy e implementarlo nei progetti aziendali era ancora lontano dalla conclusione. Siamo quindi partiti per affrontare la risoluzione di queste due importanti questioni.<\/p>\n<h2>Maggiore produttivit\u00e0!<\/h2>\n<p>\nI controlli incrementali hanno accelerato mypy, ma questo strumento era ancora troppo lento. Molti controlli incrementali duravano circa un minuto. La causa di ci\u00f2 erano le importazioni cicliche. Questo, probabilmente, non sorprender\u00e0 chi ha gi\u00e0 lavorato con grandi basi di codice scritte in Python. Avevamo set di centinaia di moduli, ognuno dei quali importava indirettamente tutti gli altri. Se un qualsiasi file all'interno di un ciclo di importazione veniva modificato, a mypy toccava elaborare tutti i file che rientravano in quel ciclo, e spesso anche tutti i moduli che importavano moduli da quel ciclo. Uno di questi cicli era il famigerato<\/p>\n<p>Abbiamo considerato la possibilit\u00e0 di \"disintrecciare\" le dipendenze circolari, ma non avevamo le risorse per farlo. C'era troppo codice con il quale non eravamo familiari. Alla fine, abbiamo adottato un approccio alternativo. Abbiamo deciso di fare in modo che mypy funzionasse rapidamente anche in presenza di \"gomitoli di dipendenze\". Abbiamo raggiunto questo obiettivo grazie a un demone mypy. Il demone \u00e8 un processo server che implementa due interessanti funzionalit\u00e0. In primo luogo, mantiene in memoria informazioni su tutta la base di codice. Ci\u00f2 significa che ogni volta che viene eseguito mypy, non \u00e8 necessario caricare i dati memorizzati nella cache relativi a migliaia di dipendenze importate. In secondo luogo, analizza attentamente, a livello di piccole unit\u00e0 strutturali, le dipendenze tra funzioni e altre entit\u00e0. Ad esempio, se una funzione <code>foo<\/code> chiama la funzione <code>bar<\/code>, c'\u00e8 una dipendenza <code>foo<\/code> di <code>bar<\/code>. Quando un file viene modificato, il demone prima elabora in isolamento solo il file modificato. Poi osserva le modifiche di quel file visibili dall'esterno, come le firme delle funzioni modificate. Il demone utilizza informazioni dettagliate sulle importazioni solo per ricontrollare quelle funzioni che utilizzano davvero la funzione modificata. Di solito, con questo approccio, le funzioni da controllare sono davvero poche.<\/p>\n<p>Implementare tutto ci\u00f2 non \u00e8 stato facile, dato che l'implementazione originale di mypy era fortemente focalizzata su l'elaborazione di un file alla volta. Abbiamo dovuto affrontare molte situazioni limite, la cui insorgenza richiedeva verifiche ripetute quando veniva modificato qualcosa nel codice. Ad esempio, questo accade quando a una classe viene assegnata una nuova classe base. Dopo aver fatto ci\u00f2 che volevamo, siamo riusciti a ridurre il tempo di esecuzione della maggior parte delle verifiche incrementali a pochi secondi. Ci \u00e8 sembrato un grande successo.<\/p>\n<h2>Ancora pi\u00f9 prestazioni!<\/h2>\n<p>\nInsieme alla memorizzazione nella cache remota di cui ho parlato sopra, il demone mypy ha praticamente risolto completamente i problemi che si presentano quando un programmatore esegue frequentemente un controllo dei tipi apportando modifiche a un numero ridotto di file. Tuttavia, le prestazioni del sistema, nel suo utilizzo meno favorevole, erano ancora lontane dall'essere ottimali. Un avvio pulito di mypy poteva richiedere pi\u00f9 di 15 minuti. E questo era molto pi\u00f9 di quanto avremmo tollerato. Ogni settimana la situazione peggiorava, poich\u00e9 i programmatori continuavano a scrivere nuovo codice e ad aggiungere annotazioni al codice esistente. I nostri utenti desideravano ancora maggiore prestazione e noi eravamo felici di andare incontro a questa esigenza.<\/p>\n<p>Abbiamo deciso di tornare a una delle prime idee riguardanti mypy. In particolare, alla trasformazione del codice Python in codice C. Gli esperimenti con Cython (sistema che consente di tradurre il codice scritto in Python in codice C) non ci hanno fornito un'accelerazione visibile, quindi abbiamo deciso di riportare in vita l'idea di scrivere un nostro compilatore. Poich\u00e9 il codice sorgente di mypy (scritto in Python) conteneva gi\u00e0 tutte le necessarie annotazioni sui tipi, ci sembrava valido tentare di utilizzare queste annotazioni per accelerare il funzionamento del sistema. Ho creato rapidamente un prototipo per testare questa idea. Ha mostrato un incremento della prestazione di oltre 10 volte in diversi micro-benchmark. La nostra idea era quella di compilare i moduli Python in moduli C utilizzando Cython, e di trasformare le annotazioni sui tipi in controlli dei tipi eseguiti durante l'esecuzione del programma (normalmente, le annotazioni sui tipi vengono ignorate durante l'esecuzione dei programmi e utilizzate solo dai sistemi di verifica dei tipi). In sostanza, pianificavamo di trasferire l'implementazione di mypy da Python a un linguaggio che fosse staticamente tipizzato, che apparisse (e, per la maggior parte, funzionasse) esattamente come Python. (Questa forma di migrazione interlinguistica \u00e8 diventata una sorta di tradizione nel progetto mypy. L'implementazione iniziale di mypy \u00e8 stata scritta in Alore, poi \u00e8 seguito un ibrido sintattico tra Java e Python.)<\/p>\n<p>L'orientamento verso l'API delle estensioni CPython \u00e8 stato fondamentale per non perdere opportunit\u00e0 nella gestione del progetto. Non \u00e8 stato necessario implementare una macchina virtuale o librerie di cui mypy necessitava. Inoltre, avremmo ancora avuto accesso all'intero ecosistema Python e a tutti gli strumenti (come pytest). Ci\u00f2 significava che avremmo potuto continuare a utilizzare codice Python interpretato durante lo sviluppo, permettendoci di continuare a lavorare con un sistema di modifica e test del codice molto veloce, senza dover aspettare la compilazione del codice. Sembra che siamo stati in grado, cos\u00ec da dire, di rimanere su due sedia contemporaneamente, e a noi piaceva.<\/p>\n<p>Il compilatore che abbiamo chiamato mypyc (poich\u00e9 utilizza mypy come frontend per l'analisi dei tipi) si \u00e8 rivelato un progetto piuttosto di successo. In generale, abbiamo raggiunto un'accelerazione di circa 4 volte nei frequenti avvii di mypy senza l'uso di caching. Lo sviluppo del nucleo del progetto mypyc ha richiesto a un piccolo team, composto da Michael Sullivan, Ivan Levkivskyi, Hugh Han e me, circa 4 mesi di calendario. Questo lavoro \u00e8 stato decisamente meno vasto rispetto a quello che sarebbe stato necessario per riscrivere mypy, ad esempio, in C++ o in Go. Inoltre, le modifiche al progetto che abbiamo dovuto apportare sono state notevolmente inferiori a quelle necessarie per una riscrittura in un'altra lingua. Inoltre, speravamo di poter portare mypyc a un livello tale da poter essere utilizzato per compilare e accelerare il proprio codice da altri programmatori di Dropbox.<\/p>\n<p>Per raggiungere un simile livello di prestazioni, abbiamo dovuto applicare alcune soluzioni ingegneristiche interessanti. Ad esempio, il compilatore pu\u00f2 accelerare l'esecuzione di molte operazioni grazie all'uso di rapide costruzioni C a basso livello. Ad esempio, la chiamata a una funzione compilata viene tradotta in una chiamata a una funzione C. Questa chiamata viene eseguita molto pi\u00f9 velocemente rispetto a una chiamata a una funzione interpretata. Alcune operazioni, come la ricerca nei dizionari, si sono ancora ridotte all'uso delle normali chiamate all'API C da CPython, che dopo la compilazione si sono rivelate solo leggermente pi\u00f9 veloci. Siamo riusciti a liberarci dell'overhead creato dall'interpretazione, ma questo ha portato solo a un piccolo guadagno in termini di prestazioni.<\/p>\n<p>Per identificare le operazioni \"lente\" pi\u00f9 comuni, abbiamo eseguito il profiling del codice. Armati dei dati ottenuti, abbiamo cercato di \"ottimizzare\" mypyc in modo che generasse un codice C pi\u00f9 veloce per tali operazioni, oppure di riscrivere il codice Python corrispondente utilizzando operazioni pi\u00f9 veloci (e a volte non avevamo semplicemente una soluzione facile per ciascun problema). Riscrivere il codice Python si \u00e8 spesso dimostrato una soluzione pi\u00f9 facile al problema rispetto all'implementazione di un'esecuzione automatica della stessa trasformazione nel compilatore. A lungo termine, volevamo automatizzare molte di queste trasformazioni, ma in quel momento ci siamo concentrati nel far avanzare mypy con il minimo sforzo. E, procedendo verso questo obiettivo, abbiamo tagliato alcuni angoli.<\/p>\n<p>Continua\u2026<\/p>\n<p><b>Gentili lettori!<\/b> Quali sono state le tue impressioni sul progetto mypy quando hai scoperto la sua esistenza?<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/vps_start\/\"><img decoding=\"async\" alt=\"Il percorso per il controllo dei tipi di 4 milioni di righe di codice Python. Parte 2\" src=\"\/wp-content\/uploads\/2019\/09\/e3c1dd40228b56d0196fd65e4df22a59.jpeg\" style=\"display:block;margin: 0 auto;\" \/><\/a><\/noindex><br \/>\n<noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/ru-rub\/#order\"><img decoding=\"async\" alt=\"Il percorso per il controllo dei tipi di 4 milioni di righe di codice Python. Parte 2\" src=\"\/wp-content\/uploads\/2019\/09\/263173dd3ab487da330ff9063e3c180e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><\/a><\/noindex><br \/>\n<br \/>Fonte: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/468235\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u0435\u043c \u0432\u0442\u043e\u0440\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0430 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0432 Dropbox \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u043b\u0438 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c \u0442\u0438\u043f\u043e\u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043c\u0438\u043b\u043b\u0438\u043e\u043d\u043e\u0432 \u0441\u0442\u0440\u043e\u043a Python-\u043a\u043e\u0434\u0430. \u2192 \u0427\u0438\u0442\u0430\u0442\u044c \u043f\u0435\u0440\u0432\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u041e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u0430\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0442\u0438\u043f\u043e\u0432 (PEP 484) \u041c\u044b \u043f\u0440\u043e\u0432\u0435\u043b\u0438 \u043f\u0435\u0440\u0432\u044b\u0435 \u0441\u0435\u0440\u044c\u0451\u0437\u043d\u044b\u0435 \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u044b \u0441 mypy \u0432 Dropbox \u0432\u043e \u0432\u0440\u0435\u043c\u044f Hack Week 2014. Hack Week \u2014 \u044d\u0442\u043e \u043c\u0435\u0440\u043e\u043f\u0440\u0438\u044f\u0442\u0438\u0435, \u043f\u0440\u043e\u0432\u043e\u0434\u0438\u043c\u043e\u0435 Dropbox \u0432 \u0442\u0435\u0447\u0435\u043d\u0438\u0435 \u043e\u0434\u043d\u043e\u0439 \u043d\u0435\u0434\u0435\u043b\u0438. \u0412 \u044d\u0442\u043e \u0432\u0440\u0435\u043c\u044f [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":28879,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-38479","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.1.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u0435\u043c \u0432\u0442\u043e\u0440\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0430 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0432 Dropbox \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u043b\u0438 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c \u0442\u0438\u043f\u043e\u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043c\u0438\u043b\u043b\u0438\u043e\u043d\u043e\u0432 \u0441\u0442\u0440\u043e\u043a Python-\u043a\u043e\u0434\u0430.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"it_IT\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u041f\u0443\u0442\u044c \u043a \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0435 \u0442\u0438\u043f\u043e\u0432 4 \u043c\u0438\u043b\u043b\u0438\u043e\u043d\u043e\u0432 \u0441\u0442\u0440\u043e\u043a Python-\u043a\u043e\u0434\u0430. \u0427\u0430\u0441\u0442\u044c 2 | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u0435\u043c \u0432\u0442\u043e\u0440\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0430 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0432 Dropbox \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u043b\u0438 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c \u0442\u0438\u043f\u043e\u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043c\u0438\u043b\u043b\u0438\u043e\u043d\u043e\u0432 \u0441\u0442\u0440\u043e\u043a Python-\u043a\u043e\u0434\u0430.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-10-31T19:23:56+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2019-10-31T19:23:56+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Il percorso verso la verifica dei tipi di 4 milioni di righe di codice Python. Parte 2 | ProHoster","description":"Oggi pubblichiamo la seconda parte della traduzione del materiale su come Dropbox ha organizzato il controllo dei tipi di diverse milioni di righe di codice Python.","canonical_url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"it_IT","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u041f\u0443\u0442\u044c \u043a \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0435 \u0442\u0438\u043f\u043e\u0432 4 \u043c\u0438\u043b\u043b\u0438\u043e\u043d\u043e\u0432 \u0441\u0442\u0440\u043e\u043a Python-\u043a\u043e\u0434\u0430. \u0427\u0430\u0441\u0442\u044c 2 | ProHoster","og:description":"\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u0435\u043c \u0432\u0442\u043e\u0440\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0430 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0432 Dropbox \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u043b\u0438 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c \u0442\u0438\u043f\u043e\u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043c\u0438\u043b\u043b\u0438\u043e\u043d\u043e\u0432 \u0441\u0442\u0440\u043e\u043a Python-\u043a\u043e\u0434\u0430.","og:url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-10-31T19:23:56+00:00","article:modified_time":"2019-10-31T19:23:56+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"38479","title":null,"description":null,"keywords":null,"keyphrases":{"focus":[],"additional":[]},"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-01-23 22:13:19","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-03-01 01:08:32","updated":"2026-08-11 12:50:31","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/38479","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/comments?post=38479"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/38479\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media\/28879"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media?parent=38479"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/categories?post=38479"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/tags?post=38479"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}