Diferencias entre los archivos Python .pyc, .pyd y .pyo

En este artículo, repasaremos los tipos de archivo de Python .pyc, .pyo y .pyd, y cómo se utilizan para almacenar el código de bytes que otros programas de Python importarán. Yo...

En este artículo, repasaremos los tipos de archivo de Python .pyc, .pyo y .pyd, y cómo se utilizan para almacenar el código de bytes que otros programas de Python importarán.

Es posible que haya trabajado con archivos .py escribiendo código Python, pero desea saber qué hacen estos otros tipos de archivos y dónde se usan. Para comprender esto, veremos cómo Python transforma el código que escribe en instrucciones que la máquina puede ejecutar directamente.

Bytecode y la máquina virtual Python

Python se envía con un intérprete que se puede usar como REPL (read-eval-print-loop), de forma interactiva, en la línea de comandos. Alternativamente, puede invocar a Python con scripts de código de Python. En ambos casos, el intérprete analiza su entrada y luego la compila en código de bytes (instrucciones de máquina de nivel inferior) que luego es ejecutado por un "Pythonic representación" del ordenador. Esta representación pythonica se llama máquina virtual Python.

Sin embargo, difiere lo suficiente de otras máquinas virtuales como la máquina virtual Java o la máquina virtual Erlang que merece su propio estudio. La máquina virtual, a su vez, interactúa con el sistema operativo y el hardware real para ejecutar las instrucciones de la máquina nativa.

Lo más importante que debe tener en cuenta cuando vea los tipos de archivo .pyc, .pyo y .pyd es que estos son archivos creados por el intérprete de Python cuando transforma el código en código de bytes compilado. La compilación del código fuente de Python en código de bytes es un paso intermedio necesario en el proceso de traducción de instrucciones del código fuente en un lenguaje legible por humanos a instrucciones de máquina que su sistema operativo puede ejecutar.

A lo largo de este artículo, analizaremos cada tipo de archivo de forma aislada, pero primero proporcionaremos información básica sobre la máquina virtual de Python y el código de bytes de Python.

El tipo de archivo .pyc

Consideramos primero el tipo de archivo .pyc. Los archivos de tipo .pyc son generados automáticamente por el intérprete cuando importa un módulo, lo que acelera la importación futura de ese módulo . Por lo tanto, estos archivos solo se crean a partir de un archivo .py si se importa mediante otro archivo o módulo .py.

Aquí hay un módulo Python de ejemplo que queremos importar. Este módulo calcula factoriales.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# math_helpers.py

# a function that computes the nth factorial, e.g. factorial(2)
def factorial(n):
    if n == 0:
        return 1
    else:
        return n * factorial(n - 1)

# a main function that uses our factorial function defined above
def main():
    print("I am the factorial helper")
    print("you can call factorial(number) where number is any integer")
    print("for example, calling factorial(5) gives the result:")
    print(factorial(5))

# this runs when the script is called from the command line
if __name__ == '__main__':
    main()

Ahora, cuando simplemente ejecuta este módulo desde la línea de comando, usando python math_helpers.py, no se crean archivos .pyc.

Importemos ahora esto en otro módulo, como se muestra a continuación. Estamos importando la función factorial del archivo math_helpers.py y usándola para calcular el factorial de 6.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# computations.py

# import from the math_helpers module
from math_helpers import factorial

# a function that makes use of the imported function
def main():
    print("Python can compute things easily from the REPL")
    print("for example, just write : 4 * 5")
    print("and you get: 20.")
    print("Computing things is easier when you use helpers")
    print("Here we use the factorial helper to find the factorial of 6")
    print(factorial(6))

# this runs when the script is called from the command line
if __name__ == '__main__':
    main()

Podemos ejecutar este script invocando python computations.py en la terminal. No solo obtenemos el resultado de 6 factorial, es decir, 720, sino que también notamos que el intérprete crea automáticamente un archivo math_helpers.pyc. Esto sucede porque el módulo cálculos importa el módulo math_helpers. Para acelerar la carga del módulo importado en el futuro, el intérprete crea un archivo de código de bytes del módulo.

Cuando se actualiza el archivo de código fuente, también se actualiza el archivo .pyc. Esto sucede cada vez que la hora de actualización del código fuente difiere de la del archivo de código de bytes y garantiza que el código de bytes esté actualizado.

Tenga en cuenta que el uso de archivos .pyc solo acelera la carga de su programa, no la ejecución real del mismo. Lo que esto significa es que puede mejorar el tiempo de inicio escribiendo su programa principal en un módulo que es importado por otro módulo más pequeño. Sin embargo, para obtener mejoras de rendimiento más generales, deberá buscar técnicas como la optimización de algoritmos y el análisis algorítmico.

Debido a que los archivos .pyc son independientes de la plataforma, se pueden compartir entre máquinas de diferentes arquitecturas. Sin embargo, si los desarrolladores tienen diferentes horas de reloj en sus sistemas, la verificación de los archivos .pyc en el control de código fuente puede crear marcas de tiempo que son efectivamente en el futuro para las lecturas de tiempo de otros. Como tal, las actualizaciones del código fuente ya no provocan cambios en el código de bytes. Esto puede ser un error desagradable de descubrir. La mejor manera de evitarlo es agregar archivos .pyc a la lista de ignorados en su sistema de control de versiones.

El tipo de archivo .pyo

El tipo de archivo .pyo también lo crea el intérprete cuando se importa un módulo. Sin embargo, el archivo .pyo resulta de ejecutar el intérprete cuando la configuración de optimización está habilitada.

El optimizador se habilita agregando el indicador "-O" cuando invocamos al intérprete de Python. Aquí hay un ejemplo de código para ilustrar el uso de la optimización. Primero, tenemos un módulo que define una lambda. En Python, una lambda es como una función, pero se define de manera más sucinta.

1
2
3
4
# lambdas.py

# a lambda that returns double whatever number we pass it
g = lambda x: x * 2

Si recuerda el ejemplo anterior, necesitaremos importar este módulo para poder usarlo. En la siguiente lista de códigos, importamos lambdas.py y hacemos uso de g lambda.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# using_lambdas.py

# import the lambdas module
import lambdas

# a main function in which we compute the double of 7
def main():
    print(lambdas.g(7))

# this executes when the module is invoked as a script at the command line
if __name__ == '__main__':
    main()

Ahora llegamos a la parte crítica de este ejemplo. En lugar de invocar Python normalmente como en el último ejemplo, aquí haremos uso de la optimización. Tener el optimizador habilitado crea archivos de código de bytes más pequeños que cuando no se usa el optimizador.

Para ejecutar este ejemplo usando el optimizador, invoque el comando:

1
$ python -O using_lambdas.py

No solo obtenemos el resultado correcto de duplicar 7, es decir, 14, como salida en la línea de comando, sino que también vemos que se crea automáticamente un nuevo archivo de código de bytes para nosotros. Este archivo se basa en la importación de lambdas.py en la invocación de using_lambdas.py. Debido a que teníamos habilitado el optimizador, se crea un archivo de código de bytes .pyo. En este caso, se llama lambdas.pyo.

El optimizador, que no hace mucho, elimina declaraciones de afirmación de su código de bytes. El resultado no se notará en la mayoría de los casos, pero puede haber ocasiones en las que lo necesite.

También tenga en cuenta que, dado que se crea un archivo de código de bytes .pyo, sustituye al archivo .pyc que se habría creado sin optimización. Cuando se actualiza el archivo de código fuente, el archivo .pyo se actualiza cada vez que la hora de actualización del código fuente difiere de la del archivo de código de bytes.

El tipo de archivo .pyd

El tipo de archivo .pyd, a diferencia de los dos anteriores, es específico de la plataforma para la clase de sistemas operativos Windows. Por lo tanto, se puede encontrar comúnmente en las ediciones personal y empresarial de Windows 10, 8, 7 y otras.

En el ecosistema de Windows, un archivo .pyd es un archivo de biblioteca que contiene código de Python que otras aplicaciones de Python pueden llamar y utilizar. Para que esta biblioteca esté disponible para otros programas de Python, se empaqueta como una biblioteca de enlaces dinámicos.

Bibliotecas de enlaces dinámicos (DLL) son bibliotecas de código de Windows que están vinculadas a programas de llamada *en tiempo de ejecución *. La principal ventaja de vincular bibliotecas en tiempo de ejecución como las DLL es que facilita la reutilización del código, las arquitecturas modulares y el inicio más rápido del programa. Como resultado, las DLL brindan una gran cantidad de funciones en los sistemas operativos Windows.

Un archivo .pyd es una biblioteca de vínculos dinámicos que contiene un módulo de Python, o un conjunto de módulos, para ser llamado por otro código de Python. Para crear un archivo .pyd, debe crear un módulo denominado, por ejemplo, example.pyd. En este módulo, deberá crear una función llamada PyInit_example(). Cuando los programas llaman a esta biblioteca, deben invocar import foo y se ejecutará la función PyInit_example().

Para obtener más información sobre cómo crear sus propios archivos Python .pyd, consulte Este artículo.

Diferencias entre estos tipos de archivo

Si bien existen algunas similitudes entre estos tipos de archivos, también existen grandes diferencias. Por ejemplo, mientras que los archivos .pyc y .pyo son similares en el sentido de que contienen el código de bytes de Python, se diferencian en que los archivos .pyo son más compactos gracias a las optimizaciones realizadas por el intérprete.

El tercer tipo de archivo, el .pyd, se diferencia de los dos anteriores por ser una biblioteca vinculada dinámicamente para ser utilizada en el sistema operativo Windows. Los otros dos tipos de archivos se pueden usar en cualquier sistema operativo, no solo en Windows.

Cada uno de estos tipos de archivos, sin embargo, involucra código que es llamado y usado por otros programas de Python.

Conclusión

En este artículo, describimos cómo la máquina virtual de Python usa cada tipo de archivo especial, .pyc, .pyo y .pyd, para reutilizar el código. Cada archivo, como vimos, tiene sus propios propósitos y casos de uso especiales, ya sea para acelerar la carga del módulo, acelerar la ejecución o facilitar la reutilización del código en ciertos sistemas operativos.