Python es asombroso.
Sorprendentemente, esa es una declaraciĆ³n bastante ambigua. ¿A quĆ© me refiero con ‘Python’?, ¿Me refiero a la interfaz abstracta de Python?, ¿Me refiero a CPython, la implementaciĆ³n comĆŗn de Python (y no confundir con Cython, que son similares en sus nombres)?, ¿O me refiero a algo completamente distinto? Tal vez me estĆ© refiriendo indirectamente a Jython, o IronPython, o PyPy. O tal vez me he ido al extremo y estoy hablando de RPython o RubyPython (los cuales son cosas muy, muy distintas).
Mientras las tecnologĆas mencionadas anteriormente son llamadas de formas parecidas y referenciadas de la misma manera, algunas de ellas sirven para propĆ³sitos completamente distintos (o, al menos, operan de maneras completamente distintas).
A lo largo de mi tiempo trabajando con Python, me topĆ© con toneladas de estas herramientas .*ython. Pero no hasta hace poco me tomĆ© el tiempo de entender quĆ© es lo que son, cĆ³mo funcionan y por quĆ© son necesarias (a sus maneras).
En este artĆculo, voy a empezar desde cero y recorrerĆ© varias implementaciones de Python, concluyendo con una introducciĆ³n detallada a PyPy, el cual creo es el futuro del lenguaje.
Todo empieza con entender que es lo que ‘Python’ realmente es.
Si tienes un buen entendimiento sobre cĆ³digo binario, mĆ”quinas virtuales y parecidos, siĆ©ntete libre de saltarte esta parte.
“Python es interpretado o compilado?”
Este es un punto comĆŗn de confusiĆ³n para principiantes en Python.
La primera cosa que hay que saber es que ‘Python’ es una interfaz. Existe una
especificaciĆ³n sobre lo que Python deberĆa hacer y cĆ³mo
deberĆa comportarse (cĆ³mo con cualquier interfaz). Y hay mĆŗltiples implementaciones (como en cualquier interfaz).
Lo segundo que hay que saber es que ‘interpretado’ y ‘compilado’ son propiedades de una implementaciĆ³n, no de una interfaz.
Entonces, la pregunta no estĆ” realmente bien formada.
¿Python es interpretado o compilado? La pregunta no estĆ” realmente bien formada.
Dicho esto, para la implementaciĆ³n mĆ”s comĆŗn (CPython: escrito en C, usualmente llamado simplemente ‘Python’, y seguramente lo que estĆ”s usando si no tienes idea de lo que estoy hablando), la respuesta es:interpretado, con algunas partes compiladas. CPython compila** el cĆ³digo fuente de Python a *bytecode, y en ese momento interpreta ese bytecode, ejecutĆ”ndolo sobre la marcha.
* Nota: no es una ‘compilaciĆ³n’ en sentido tradicional de la palabra. Normalmente, decimos que ‘compilar’ es tomar el cĆ³digo de alto nivel y convertirlo en cĆ³digo binario. Pero es un tipo de ‘compilaciĆ³n’.
Veamos la respuesta un poco mĆ”s de cerca, ya que nos permitirĆ” entender algunos de los conceptos que surgirĆ”n mĆ”s adelante en el artĆculo.
Bytecode vs. cĆ³digo binario
Es muy importante entender la diferencia entre bytecode y cĆ³digo binario (o nativo), tal vez mejor ilustrada con ejemplos:
- C compila a cĆ³digo binario, que luego es ejecutado directamente en tu procesador. Cada instrucciĆ³n le indica a tu CPU que mueva cosas alrededor.
- Java compila a bytecode, que luego es ejecutado en la mĆ”quina virtual de Java(Java Virtual Machine, Ć³ JVM), una abstracciĆ³n de una computadora que ejecuta programas. Cada instrucciĆ³n es entonces manejada por la JVM, que interactĆŗa con tu computadora.
En tĆ©rminos breves: cĆ³digo binario es mĆ”s rĆ”pido, pero bytecode es mĆ”s portable y seguro.
El cĆ³digo binario se ve distinto, dependiendo de tu mĆ”quina, pero bytecode se ve igual en todas las maquinas. Se podrĆa decir que el cĆ³digo binario estĆ” optimizado para tu configuracion.
Volviendo a CPython, el proceso en el conjunto de herramientas sucede de la siguiente manera:
- CPython compila tu cĆ³digo Python a bytecode
- Ese bytecode es entonces ejecutado en la MƔquina Virtual CPython
Los principiantes asumen que Python es compilado a raĆz de los archivos .pyc. Hay alguna verdad en esto: el archivo .pyc es bytecode compilado, que es despuĆ©s interpretado. Entonces si haz ejecutado cĆ³digo Python y ya tienes un archivo .pyc disponible, el mismo va a ejecutarse mĆ”s rĆ”pido la segunda vez ya que no necesitarĆ” recompilar el bytecode.
Maquinas virtuales alternativas: Jython, IronPython, y mƔs
CĆ³mo mencionĆ© anteriormente, Python tiene varias implementaciones. De vuelta, como mencionĆ© antes, la mĆ”s comĆŗn es CPython. Ćsta es una implementaciĆ³n de Python escrita en C y es considerada la implementaciĆ³n ‘por defecto’.
¿Pero, quĆ© pasa con las alternativas? Una de las mĆ”s prominentes es
Jython, una implementaciĆ³n en Java que utiliza la JVM. Mientras CPython produce bytecode para ser corrido en la VM de CPython, Jython produce
bytecode de Java para correr en la JVM (esto es lo mismo que es producido cuando se compila un programa en Java).
“¿Por quĆ© usarĆa alguna vez una implementaciĆ³n alternativa?”, podrĆas preguntar. Bueno, para empezar, esasdiferentes implementaciones juegan muy bien con diferentes conjuntos de tecnologĆas.
CPython hace muy fĆ”cil el escribir extensiones C para tu cĆ³digo Python porque al final es ejecutado por un intĆ©rprete de C. Por otro lado, Jython, facilita trabajar con otros programas en Java: puedes importar cualquier clase de Java sin mayor esfuerzo, evocando y utilizando tus clases Java dentro tus programas Jython.
(Nota aparte: si no pensaste en esto detalladamente, es una locura. Estamos en un punto donde puedes mezclar y triturar diferentes lenguajes y compilarlos todos en una misma esencia. Como fue mencionado por Rostin, los programas que mezclan cĆ³digo Fortran y C estĆ”n desde hace un tiempo. AsĆ que, por supuesto que esto no es algo necesariamente nuevo. Pero sigue siendo genial.)
CĆ³mo ejemplo, esto es cĆ³digo Jython vĆ”lido:
[Java HotSpot(TM) 64-Bit Server VM (Apple Inc.)] on java1.6.0_51
>>> from java.util import HashSet
>>> s = HashSet(5)
>>> s.add("Foo")
>>> s.add("Bar")
>>> s
[Foo, Bar]
IronPython es otra implementaciĆ³n popular de Python, escrita enteramente en C# y apuntando a la tecnologĆa .NET. En particular, corre con lo que se podrĆa llamar la MĆ”quina Virtual .NET,
Common Language Runtime (CLR)de Microsoft, comparable con la JVM.
PodrĆas decir que Jython : Java :: IronPython : C#. Corren en sus respectivas VMs, puedes importar clases C# en tu cĆ³digo IronPython y clases Java desde tu cĆ³digo Jython, etc.
Es totalmente posible sobrevivir sin tocar alguna vez una implementaciĆ³n de Python no-CPython. Pero hay ventajas que se obtienen desde el cambio, muchas de ellas son dependientes de la tecnologĆa que uses. ¿Usas muchos lenguajes basados en la JVM? Jython puede ser para tĆ. ¿Todo lo que haces es sobre la tecnologĆa .NET? Tal vez debas probar IronPython (y tal vez ya lo hayas hecho).
Por cierto: mientras que esto no serĆa una razĆ³n para usar una implementaciĆ³n diferente, nota que estas implementaciones sĆ difieren en comportamiento mĆ”s allĆ” de como tratan tu cĆ³digo fuente en Python. Sin embargo, esas diferencias son comĆŗnmente menores, y se disuelven o emergen con el tiempo mientras estas implementaciones se encuentran bajo un activo desarrollo. Por ejemplo, IronPython
usa cadenas Unicode por defecto; Sin embargo, CPython,
por defecto usa ASCII para versiones 2.x (fallando con un error de codificaciĆ³n
UnicodeEncodeError para caracteres no-ASCII), pero sĆ soporta cadenas
Unicode por defecto para las versiones 3.x.
CompilaciĆ³n Justo-a-Tiempo: PyPy y el futuro
Por lo tanto, tenemos una implementaciĆ³n de Python escrita en C, una en Java una en C#. El prĆ³ximo paso lĆ³gico: una implementaciĆ³n de Python escrita en… Python. (El lector educado encontrarĆ” esta notaciĆ³n levemente engaƱosa).
AquĆ es donde las cosas se ponen confusas. Primero, discutamos sobre compilaciĆ³n Justo-a-Tiempo (Just-in-Time, Ć³ JIT).
JIT: El por quĆ© y el cĆ³mo
Recordemos que el cĆ³digo binario es mucho mĆ”s rĆ”pido que bytecode. Bueno, ¿y si pudiĆ©ramos compilar algunas partes de nuestro bytecode y luego correrlo como cĆ³digo nativo? TendrĆamos que pagar algĆŗn precio al compilar a bytecode (por ej., tiempo), pero si el resultado fuese mĆ”s rĆ”pido, eso serĆa genial! Esa es la motivaciĆ³n de la compilaciĆ³n JIT, una tĆ©cnica hĆbrida que mezcla los beneficios de los interpretadores y los compiladores. En tĆ©rminos bĆ”sicos, JIT quiere utilizar compilaciĆ³n para acelerar un sistema interpretado.
Por ejemplo, un enfoque comĆŗn tomado por la compilaciĆ³n JIT:
- Identificar bytecode que es ejecutado frecuentemente.
- Compilar a cĆ³digo binario.
- Almacenar el resultado en memoria cachƩ.
- Siempre que el mismo bytecode sea encontrado para ejecutar, en vez de usarlo, ejecutar el cĆ³digo binario precompilado y cosechar los beneficios (por ej., aumentos de velocidad)
De esto se trata PyPy: llevar JIT a Python (mira el ApĆ©ndice para ver esfuerzos anteriores). Hay, por supuesto, otros objetivos: PyPy apunta a ser multiplataforma, bajo en consumo de memoria e independiente del conjunto de tecnologĆas. Pero JIT realmente se vende por si solo. Como promedio de un puƱado de pruebas de tiempo, se dice que mejora el rendimiento a un factor de
6.27. Para un mayor anƔlisis, vƩase este cuadro del
PyPy Speed Center:
PyPy es difĆcil de entender
Pero hay mucha confusiĆ³n alrededor de PyPy (vĆ©ase, por ejemplo, esta propuesta sin sentido para crear un
PyPyPy…). En mi opiniĆ³n, eso es principalmente porque PyPy es actualmente dos cosas:
Un intƩrprete de Python escrito en
RPython (no Python (he mentido antes). RPython es un subconjunto de Python con tipos estĆ”ticos. En Python, es “
prĆ”cticamente imposible” razonar rigurosamente acerca de tipos (¿Por que es tan difĆcil? Bueno, considera el hecho que:
x = random.choice([1, "foo"])
serĆa cĆ³digo Python vĆ”lido (crĆ©ditos a
Ademan). ¿De quĆ© tipo es x? ¿CĆ³mo podemos razonar acerca de tipos de variables cuando los tipos ni siquiera son estrictamente forzados?). Con RPython, sacrificas algo de flexibilidad, pero a cambio es muchĆsimo mĆ”s fĆ”cil razonar sobre manejo de memoria y demĆ”s, lo cual permite optimizaciones.
Un compilador que compila cĆ³digo RPython para varios objetivos y agrega JIT. La plataforma por defecto es C, por ej., un compilador RPython-a-C, pero tambiĆ©n puedes apuntar a JVM y otros.
Ćnicamente para mayor claridad, me referirĆ© a ellos como PyPy (1) y PyPy (2).
¿Por quĆ© necesitarĆas esas dos cosas, y por quĆ© bajo el mismo techo? PiĆ©nsalo de esta manera: PyPy(1) es un intĆ©rprete escrito en RPython. Entonces toma el cĆ³digo Python del usuario y lo compila a bytecode. Pero el interpretador en sĆ (escrito en RPython) tiene que ser interpretado por otra implementaciĆ³n de Python para poder correr, ¿Verdad?
Bueno, podrĆamos simplemente
usar CPython para correr el intĆ©rprete. Pero eso no serĆa lo suficientemente rĆ”pido.
En cambio, la idea es que usemos PyPy(2) (tambiĆ©n conocido cĆ³mo RPython Toolchain)-Set de herramientas RPython) para compilar al interpretador de PyPy a cĆ³digo que otra plataforma (por ej., C, JVM o CLI) pueda correr en nuestra mĆ”quina, agregando tambiĆ©n JIT. Es mĆ”gico: PyPy agrega dinĆ”micamente JIT a un interpretador, generando su propio compilador! (De vuelta, esto es una locura: estamos compilando un interpretador y agregando otro compilador independiente por separado).
Al final, el resultado es un ejecutable independiente que interpreta el cĆ³digo fuente Python y explota las optimizaciones de JIT. Que es lo que justamente querĆamos! Es un gran bocado, pero tal vez este diagrama ayude:
Reiterando, la verdadera belleza de PyPy es que podemos escribir nosotros mismos un puƱado de interpretadores Python distintos en RPython sin preocuparnos por JIT (salvo algunas sugerencias). PyPy entonces implementarĆa JIT por nosotros usando el set de herramientas de RPython/PyPy(2).
De hecho, si nos ponemos aĆŗn mĆ”s abstractos, podrĆas, teĆ³ricamente, escribir un interpretador para cualquierlenguaje, alimentar a PyPy con Ć©l, y obtener un JIT para ese lenguaje. Esto es porque PyPy se enfoca en optimizar el interpretador actual, en vez de los detalles del lenguaje que estĆ” interpretando.
PodrĆas, teĆ³ricamente, escribir un interpretador para *cualquier* lenguaje, alimentar a PyPy con Ć©l, y obtener un JIT para ese lenguaje.
Divagando un poco, me gustarĆa mencionar que JIT en sĆ mismo es absolutamente fascinante. Usa una tĆ©cnica llamada tracing (Ć³ seguimiento), la cual se ejecuta
de la siguiente manera:
- Correr el interpretador e interpretar todo (sin agregar nada de JIT)
- Perfilar levemente el cĆ³digo interpretado.
- Identificar operaciones que hayas realizado antes.
- Compilar esos pedazos a cĆ³digo binario.
Para mĆ”s informaciĆ³n, este documento es altamente accesible y muy interesante.
Para ir concluyendo: usamos el compilador RPython-a-C de PyPy (u otra plataforma) para compilar el interpretador implementado RPython de PyPy.
Concluyendo
¿Por quĆ© es tan genial? ¿Por quĆ© vale la pena perseguir esta idea tan loca? Creo que
Alex Gaynor lo describiĆ³ muy bien en su
blog: “[PyPy es el futuro] porque ofrece mejor velocidad, mĆ”s flexibilidad y es una mejor plataforma para el crecimiento de Python.”
En resumen:
ApĆ©ndice: Otros nombres que tal vez hayas oĆdo
- Python 3000 (Py3k): nombre alternativo para Python 3.0, un mayor, compatible-con-versiones-anterioreslanzamiento de Python que alcanzĆ³ la escena en 2008. El equipo de Py3k predijo que llevarĆa alrededor decinco anos para que esta versiĆ³n sea completamente adoptada. Y mientras que la mayorĆa (cuidado: se dice que es anecdĆ³tico) de los desarrolladores de Python siguen usando Python 2.x, la conciencia de Py3k entre la gente estĆ” incrementĆ”ndose.
- Cython: un super set de Python que incluye bindings (Ć³ enlaces)para llamar funciones de C.
- Objetivo: permitirte escribir extensiones en C para tu cĆ³digo Python
- AdemĆ”s te permite agregar tipos de variables estĆ”ticos a tu cĆ³digo Python, permitiĆ©ndole que sea compilado y alcanzar rendimiento parecido al de C.
- Es similar a PyPy, pero no es lo mismo. En este caso, estĆ”s forzado a escribir el cĆ³digo del usuario antes de pasarlo al compilador. Con PyPy, escribes simplemente cĆ³digo Python, y el compilador maneja cualquier optimizaciĆ³n.
- Numba: : un “compilador especializado justo-a-tiempo” que agrega JIT a cĆ³digo Python anotado. En tĆ©rminos mĆ”s bĆ”sicos, le das algunas indicaciones y acelera partes de tu cĆ³digo. Numa viene como parte de la distribuciĆ³n Anaconda,un set de paquetes para manejo y anĆ”lisis de datos.
- IPython: muy diferente a todo lo que hemos discutido hasta ahora. Es un ambiente de procesamiento para Python. Interactivo y con soporte para herramientas grƔficas y experiencia de navegador, etc.
Enlaces de lenguaje
- RubyPython: un puente entre las mĆ”quinas virtuales de Ruby y Python. Permite embeber cĆ³digo de Python dentro de tu cĆ³digo de Ruby. Defines donde Python comienza y termina, y RubyPython calcula los datos entre las VMs.
- PyObjc: enlaces de lenguaje entre Python y Objetive-C, actuando como un puente entre ellos. PrĆ”cticamente, eso significa que puedes utilizar librerĆas de Objective-C (incluyendo todo lo que necesitas para crear aplicaciones de OS X) desde tu cĆ³digo Python, y mĆ³dulos de Python desde tu cĆ³digo Objective-C. En este caso, es conveniente que CPython estĆ© escrito en C, el cual es un subconjunto de Objective-C.
- PyQt: mientras PyObjc te ofrece una interfaz para los componentes grƔficos de OS X, PyQt hace lo mismo para el framework de QT, permitiendote crear completas interfaces grƔficas, acceso a bases de datos SQL, etc. Otra herramienta dirigida a traer la simplicidad de Python a otros frameworks.
Frameworks JavaScript
- pyjs (Pyjamas): un framework para crear aplicaciones web y de escritorio en Python. Incluye un compilador Python-a-Javascript, un conjunto de widgets, y algunas herramientas mƔs.
- Brython: una mĆ”quina virtual de Python escrita en JavaScript para permitir que el cĆ³digo de Py3k sea ejecutado en navegadores.
Creditos:
Fuente Original: