{"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\/es\/blog\/administrirovanie\/put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2","title":{"rendered":"El camino para verificar tipos en 4 millones de l\u00edneas de c\u00f3digo Python. Parte 2","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Hoy publicamos la segunda parte de la traducci\u00f3n sobre c\u00f3mo en Dropbox se organiz\u00f3 el control de tipos de varios millones de l\u00edneas de c\u00f3digo Python.<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/ruvds\/blog\/468235\/\"><img decoding=\"async\" alt=\"El camino para verificar tipos en 4 millones de l\u00edneas de c\u00f3digo 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\/\">Leer la primera parte<\/a><\/noindex><br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Soporte oficial de tipos (PEP 484)<\/h2>\n<p>\nRealizamos nuestras primeras experiencias serias con mypy en Dropbox durante la Hack Week de 2014. La Hack Week es un evento que se lleva a cabo en Dropbox durante una semana. Durante este tiempo, los empleados pueden trabajar en lo que quieran. Algunos de los proyectos tecnol\u00f3gicos m\u00e1s emblem\u00e1ticos de Dropbox comenzaron precisamente en estos eventos. Como resultado de este experimento, concluimos que mypy parece prometedor, aunque el proyecto a\u00fan no estaba listo para un uso general.<\/p>\n<p>En ese momento, hab\u00eda en el aire la idea de estandarizar los sistemas de sugerencias de tipos en Python. Como mencion\u00e9, a partir de Python 3.0 se pod\u00edan usar anotaciones de tipo para funciones, pero eran solo expresiones arbitrarias, sin una sintaxis y sem\u00e1ntica definidas. Durante la ejecuci\u00f3n del programa, estas anotaciones eran, en su mayor\u00eda, simplemente ignoradas. Despu\u00e9s de la Hack Week, comenzamos a trabajar en la estandarizaci\u00f3n de la sem\u00e1ntica. Este trabajo llev\u00f3 a la aparici\u00f3n de <noindex><a rel=\"nofollow\" href=\"https:\/\/www.python.org\/dev\/peps\/pep-0484\/\">PEP 484<\/a><\/noindex> (este documento fue elaborado en conjunto por Guido van Rossum, \u0141ukasz Langa y yo).<\/p>\n<p>Nuestros motivos se pueden ver desde dos perspectivas. Primero, esper\u00e1bamos que todo el ecosistema de Python pudiera adoptar un enfoque com\u00fan en el uso de sugerencias de tipos (type hints, un t\u00e9rmino usado en Python como un an\u00e1logo a \"anotaciones de tipos\"). Esto, considerando los posibles riesgos, ser\u00eda mejor que usar una multitud de enfoques mutuamente incompatibles. En segundo lugar, quer\u00edamos discutir abiertamente los mecanismos de anotaci\u00f3n de tipos con muchos miembros de la comunidad de Python. En parte, este deseo fue dictado por el hecho de que no quer\u00edamos parecer \"herejes\" de las ideas b\u00e1sicas del lenguaje ante las masas de programadores de Python. Este es un lenguaje de tipado din\u00e1mico, conocido por su \"tipado de pato\". En la comunidad, al principio, surgi\u00f3 una cierta desconfianza hacia la idea de la tipificaci\u00f3n est\u00e1tica. Pero esa actitud se debilit\u00f3 con el tiempo, una vez que qued\u00f3 claro que no se planeaba hacer obligatoria la tipificaci\u00f3n est\u00e1tica (y despu\u00e9s de que las personas se dieron cuenta de que realmente era \u00fatil).<\/p>\n<p>La sintaxis final de las sugerencias de tipos fue muy similar a la que mypy soportaba en ese momento. El documento PEP 484 fue lanzado junto con Python 3.5 en 2015. Python dej\u00f3 de ser un lenguaje que solo soportaba tipado din\u00e1mico. Me gusta pensar en este evento como un hito significativo en la historia de Python.<\/p>\n<h2>Inicio de la migraci\u00f3n<\/h2>\n<p>\nA finales de 2015, se form\u00f3 un equipo de tres personas en Dropbox para trabajar en mypy. Inclu\u00eda a Guido van Rossum, Greg Price y David Fisher. A partir de ese momento, la situaci\u00f3n comenz\u00f3 a desarrollarse extremadamente r\u00e1pido. El primer obst\u00e1culo en el camino del crecimiento de mypy fue el rendimiento. Como mencion\u00e9 anteriormente, en las primeras etapas del desarrollo del proyecto consider\u00e9 traducir la implementaci\u00f3n de mypy al lenguaje C, pero esta idea fue descartada por el momento. Est\u00e1bamos atrapados en el hecho de que para ejecutar el sistema se utilizaba el int\u00e9rprete CPython, que no ofrec\u00eda la velocidad suficiente para herramientas como mypy. (El proyecto PyPy, una implementaci\u00f3n alternativa de Python con compilador JIT, tampoco nos ayud\u00f3.)<\/p>\n<p>Afortunadamente, algunos avances algor\u00edtmicos vinieron en nuestro auxilio. El primer y poderoso \"acelerador\" fue la implementaci\u00f3n de la verificaci\u00f3n incremental. La idea de esta mejora era simple: si todas las dependencias de un m\u00f3dulo no hab\u00edan cambiado desde la \u00faltima ejecuci\u00f3n de mypy, pod\u00edamos utilizar, durante el trabajo con las dependencias, los datos en cach\u00e9 de la sesi\u00f3n anterior. Solo necesit\u00e1bamos realizar una verificaci\u00f3n de tipos en los archivos modificados y en aquellos que depend\u00edan de ellos. Mypy fue incluso un poco m\u00e1s lejos: si la interfaz externa del m\u00f3dulo no hab\u00eda cambiado, mypy consideraba que otros m\u00f3dulos que importaban este m\u00f3dulo no necesitaban ser verificados nuevamente.<\/p>\n<p>La verificaci\u00f3n incremental nos ayud\u00f3 mucho al anotar grandes vol\u00famenes de c\u00f3digo existente. El hecho es que este proceso generalmente implica m\u00faltiples ejecuciones iterativas de mypy, ya que las anotaciones se agregan gradualmente al c\u00f3digo y se mejoran poco a poco. La primera ejecuci\u00f3n de mypy a\u00fan era muy lenta, ya que durante su ejecuci\u00f3n hab\u00eda que verificar muchas dependencias. Entonces, con el objetivo de mejorar la situaci\u00f3n, implementamos un mecanismo de almacenamiento en cach\u00e9 remoto. Si mypy detecta que la cach\u00e9 local probablemente est\u00e1 desactualizada, descarga la instant\u00e1nea actual de la cach\u00e9 para toda la base de c\u00f3digo desde un repositorio centralizado. Luego, ejecuta, usando esta instant\u00e1nea, una verificaci\u00f3n incremental. Esto nos llev\u00f3 otro paso considerable hacia el aumento del rendimiento de mypy.<\/p>\n<p>Fue un per\u00edodo de adopci\u00f3n r\u00e1pida y natural del sistema de verificaci\u00f3n de tipos en Dropbox. Para finales de 2016, ya ten\u00edamos aproximadamente 420,000 l\u00edneas de c\u00f3digo Python con anotaciones de tipos. Muchos usuarios recibieron con entusiasmo la verificaci\u00f3n de tipos. En Dropbox, mypy fue utilizado cada vez m\u00e1s por equipos de desarrolladores.<\/p>\n<p>Todo parec\u00eda ir bien, pero a\u00fan ten\u00edamos mucho por hacer. Comenzamos a realizar encuestas internas peri\u00f3dicas a los usuarios para identificar los problemas del proyecto y entender cu\u00e1les preguntas deb\u00edan resolverse primero (esta pr\u00e1ctica se utiliza en la empresa hasta el d\u00eda de hoy). Como se hizo evidente, las dos tareas m\u00e1s importantes eran. Primero, necesit\u00e1bamos m\u00e1s cobertura de c\u00f3digo con tipos; segundo, necesit\u00e1bamos que mypy funcionara m\u00e1s r\u00e1pido. Era completamente claro que nuestro trabajo para acelerar mypy y su implementaci\u00f3n en los proyectos de la empresa a\u00fan estaba lejos de completarse. Con plena conciencia de la importancia de estas dos tareas, nos pusimos manos a la obra para resolverlas.<\/p>\n<h2>\u00a1M\u00e1s rendimiento!<\/h2>\n<p>\nLas verificaciones incrementales aceleraron mypy, pero esta herramienta a\u00fan no era lo suficientemente r\u00e1pida. Muchas verificaciones incrementales duraban alrededor de un minuto. La causa de esto eran las importaciones c\u00edclicas. Esto probablemente no sorprender\u00e1 a aquellos que han trabajado con grandes bases de c\u00f3digo escritas en Python. Ten\u00edamos conjuntos de cientos de m\u00f3dulos, cada uno de los cuales importaba indirectamente a todos los dem\u00e1s. Si alg\u00fan archivo dentro del ciclo de importaci\u00f3n era modificado, mypy ten\u00eda que procesar todos los archivos en ese ciclo, y frecuentemente incluso cualquier m\u00f3dulo que importara m\u00f3dulos de ese ciclo. Uno de esos ciclos era el infame<\/p>\n<p>Consideramos la posibilidad de \"desenredar\" las dependencias c\u00edclicas, pero no ten\u00edamos los recursos para hacerlo. Hab\u00eda demasiado c\u00f3digo con el que no est\u00e1bamos familiarizados. Al final, optamos por un enfoque alternativo. Decidimos hacer que mypy funcionara r\u00e1pidamente incluso con \"                                \". Alcanzamos este objetivo mediante un demonio mypy. Un demonio es un proceso del servidor que implementa dos caracter\u00edsticas interesantes. Primero, mantiene en memoria informaci\u00f3n sobre toda la base de c\u00f3digo. Esto significa que en cada ejecuci\u00f3n de mypy no es necesario cargar datos en cach\u00e9 relativos a miles de dependencias importadas. En segundo lugar, analiza cuidadosamente, a nivel de unidades estructurales peque\u00f1as, las dependencias entre funciones y otras entidades. Por ejemplo, si la funci\u00f3n <code>foo<\/code> llama a la funci\u00f3n <code>bar<\/code>, entonces hay una dependencia <code>foo<\/code> desde <code>bar<\/code>. Cuando un archivo cambia, el demonio primero procesa en aislamiento solo el archivo modificado. Luego observa los cambios en ese archivo que son visibles externamente, como las firmas de funciones que han cambiado. El demonio utiliza informaci\u00f3n detallada sobre las importaciones solo para volver a verificar aquellas funciones que realmente utilizan la funci\u00f3n modificada. Usualmente, con este enfoque, hay que verificar muy pocas funciones.<\/p>\n<p>La implementaci\u00f3n de todo esto no fue una tarea sencilla, ya que la implementaci\u00f3n original de mypy estaba muy orientada al procesamiento de un solo archivo a la vez. Tuvimos que enfrentarnos a numerosas situaciones l\u00edmite, cuya aparici\u00f3n requer\u00eda verificaciones repetidas cada vez que se realizaba un cambio en el c\u00f3digo. Por ejemplo, esto sucede cuando se asigna una nueva clase base a una clase. Despu\u00e9s de hacer lo que quer\u00edamos, logramos reducir el tiempo de ejecuci\u00f3n de la mayor\u00eda de las comprobaciones incrementales a unos pocos segundos. Para nosotros, esto fue una gran victoria.<\/p>\n<h2>\u00a1A\u00fan m\u00e1s rendimiento!<\/h2>\n<p>\nJunto con la cach\u00e9 remota, de la que habl\u00e9 antes, el demonio de mypy pr\u00e1cticamente resolvi\u00f3 los problemas que surg\u00edan cuando un programador ejecutaba a menudo la verificaci\u00f3n de tipos, haciendo cambios en un peque\u00f1o n\u00famero de archivos. Sin embargo, el rendimiento del sistema en el escenario menos favorable segu\u00eda estando lejos de ser \u00f3ptimo. Un inicio limpio de mypy pod\u00eda llevar m\u00e1s de 15 minutos. Y eso era mucho m\u00e1s de lo que nos hubiera gustado. Cada semana la situaci\u00f3n empeoraba, ya que los programadores segu\u00edan escribiendo nuevo c\u00f3digo y a\u00f1adiendo anotaciones al c\u00f3digo existente. Nuestros usuarios a\u00fan deseaban m\u00e1s rendimiento, y nosotros est\u00e1bamos encantados de complacerlos.<\/p>\n<p>Decidimos volver a una de las ideas tempranas respecto a mypy. Espec\u00edficamente, a la transformaci\u00f3n de c\u00f3digo Python en c\u00f3digo C. Los experimentos con Cython (un sistema que permite traducir c\u00f3digo escrito en Python a c\u00f3digo C) no nos dieron ning\u00fan aumento de velocidad visible, por lo que decidimos revivir la idea de escribir nuestro propio compilador. Dado que la base de c\u00f3digo de mypy (escrita en Python) ya conten\u00eda todas las anotaciones de tipo necesarias, nos pareci\u00f3 una buena idea utilizar estas anotaciones para acelerar el funcionamiento del sistema. R\u00e1pidamente cre\u00e9 un prototipo para comprobar esta idea. Este mostr\u00f3 en varios micro-benchmarks un crecimiento de rendimiento de m\u00e1s de 10 veces. Nuestra idea era compilar m\u00f3dulos de Python en m\u00f3dulos C utilizando Cython, y convertir las anotaciones de tipo en verificaciones de tipo que se realizar\u00edan en tiempo de ejecuci\u00f3n del programa (normalmente, las anotaciones de tipo se ignoran durante la ejecuci\u00f3n de los programas y son utilizadas solo por sistemas de verificaci\u00f3n de tipos). De hecho, plane\u00e1bamos trasladar la implementaci\u00f3n de mypy de Python a un lenguaje que hubiera sido creado con tipado est\u00e1tico, que se ver\u00eda (y, en su mayor parte, funcionar\u00eda) exactamente como Python. (Este tipo de migraci\u00f3n entre lenguajes se convirti\u00f3 en algo as\u00ed como una tradici\u00f3n del proyecto mypy. La implementaci\u00f3n original de mypy fue escrita en Alore, y luego fue un h\u00edbrido sint\u00e1ctico de Java y Python).<\/p>\n<p>La orientaci\u00f3n hacia la API de extensiones de CPython fue clave para no perder capacidades en la gesti\u00f3n del proyecto. No necesit\u00e1bamos implementar una m\u00e1quina virtual o cualquier biblioteca que mypy requiriera. Adem\u00e1s, a\u00fan tendr\u00edamos acceso a todo el ecosistema de Python, y estar\u00edan disponibles todas las herramientas (como pytest). Esto significaba que podr\u00edamos seguir utilizando c\u00f3digo Python interpretado a lo largo del desarrollo, lo que nos permitir\u00eda continuar trabajando con un esquema de modificaci\u00f3n y prueba de c\u00f3digo muy r\u00e1pido, en lugar de esperar a que el c\u00f3digo se compilara. Parec\u00eda que est\u00e1bamos logrando, por as\u00ed decirlo, mantenernos en dos sillas al mismo tiempo, y nos gustaba.<\/p>\n<p>El compilador que hemos llamado mypyc (ya que utiliza mypy como frontend para el an\u00e1lisis de tipos) ha demostrado ser un proyecto bastante exitoso. En general, logramos aproximadamente una aceleraci\u00f3n de cuatro veces en las ejecuciones frecuentes de mypy sin el uso de cach\u00e9. El desarrollo del n\u00facleo del proyecto mypyc tom\u00f3 alrededor de 4 meses calendario para un peque\u00f1o equipo que inclu\u00eda a Michael Sullivan, Ivan Levkiysky, Hugh Hahn y yo. Este volumen de trabajo fue mucho menos extenso que el que habr\u00eda requerido reescribir mypy, por ejemplo, en C++ o Go. Adem\u00e1s, las modificaciones que hicimos en el proyecto fueron considerablemente menores que las que habr\u00edamos tenido que realizar al reescribirlo en otro lenguaje. Tambi\u00e9n ten\u00edamos la esperanza de que pudi\u00e9ramos llevar mypyc a un nivel en el que otros programadores de Dropbox pudieran utilizarlo para compilar y acelerar su c\u00f3digo.<\/p>\n<p>Para alcanzar un nivel de rendimiento similar, tuvimos que implementar algunas soluciones de ingenier\u00eda interesantes. De este modo, el compilador puede acelerar la ejecuci\u00f3n de muchas operaciones gracias al uso de construcciones C de bajo nivel r\u00e1pidas. Por ejemplo, la llamada a una funci\u00f3n compilada se traduce en una llamada a una funci\u00f3n C. Y una llamada as\u00ed se ejecuta mucho m\u00e1s r\u00e1pido que una llamada a una funci\u00f3n interpretada. Algunas operaciones, como la b\u00fasqueda en diccionarios, a\u00fan depend\u00edan de llamados al C-API de CPython, que despu\u00e9s de la compilaci\u00f3n resultaban solo un poco m\u00e1s r\u00e1pidos. Logramos eliminara la carga adicional que genera la interpretaci\u00f3n, pero en este caso solo se tradujo en una peque\u00f1a mejora en rendimiento.<\/p>\n<p>Para identificar las operaciones \u00ablentas\u00bb m\u00e1s comunes, realizamos un perfilado del c\u00f3digo. Con los datos obtenidos, intentamos ajustar mypyc para que generara un c\u00f3digo C m\u00e1s r\u00e1pido para este tipo de operaciones, o reescribir el c\u00f3digo Python correspondiente utilizando operaciones m\u00e1s r\u00e1pidas (y a veces simplemente no ten\u00edamos una soluci\u00f3n f\u00e1cil para alg\u00fan problema en particular). Reescribir el c\u00f3digo Python a menudo result\u00f3 ser una soluci\u00f3n m\u00e1s sencilla al problema que implementar la transformaci\u00f3n autom\u00e1tica de la misma en el compilador. A largo plazo, quer\u00edamos automatizar muchas de estas transformaciones, pero en ese momento nos centramos en acelerar mypy con el m\u00ednimo esfuerzo. Y, al avanzar hacia este objetivo, recortamos algunas esquinas.<\/p>\n<p>Continuar\u00e1...<\/p>\n<p><b>\u00a1Estimados lectores!<\/b> \u00bfQu\u00e9 impresiones te caus\u00f3 el proyecto mypy cuando supiste de su existencia?<\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/ruvds.com\/vps_start\/\"><img decoding=\"async\" alt=\"El camino para verificar tipos en 4 millones de l\u00edneas de c\u00f3digo 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=\"El camino para verificar tipos en 4 millones de l\u00edneas de c\u00f3digo Python. Parte 2\" src=\"\/wp-content\/uploads\/2019\/09\/263173dd3ab487da330ff9063e3c180e.jpeg\" style=\"display:block;margin: 0 auto;\" \/><\/a><\/noindex><br \/>\n<br \/>Fuente: <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.3 - 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\/es\/blog\/administrirovanie\/put-k-proverke-tipov-4-millionov-strok-python-koda-chast-2\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"es_ES\" \/>\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\/es\/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\udd47El camino hacia la verificaci\u00f3n de tipos de 4 millones de l\u00edneas de c\u00f3digo Python. Parte 2 | ProHoster","description":"Hoy publicamos la segunda parte de la traducci\u00f3n sobre c\u00f3mo en Dropbox se organiz\u00f3 el control de tipos de varios millones de l\u00edneas de c\u00f3digo Python.","canonical_url":"https:\/\/prohoster.info\/es\/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":"es_ES","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\/es\/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\/es\/wp-json\/wp\/v2\/posts\/38479","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/comments?post=38479"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/posts\/38479\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/media\/28879"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/media?parent=38479"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/categories?post=38479"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/es\/wp-json\/wp\/v2\/tags?post=38479"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}