Preguntas frecuentes sobre bibliotecas y extensiones

Contenido

Cuestiones generales sobre bibliotecas

¿Cómo encuentro un módulo o aplicación para ejecutar la tarea X?

Vea la referencia de bibliotecas para comprobar si existe un módulo relevante en la biblioteca estándar. (Eventualmente aprenderá lo que hay en la biblioteca estándar y será capaz de saltarse este paso.)

Para los paquetes de terceros, busque en el Índice de Paquetes de Python o pruebe Google u otro motor de búsqueda. Buscando por «Python» más una o dos palabras clave del tema de interés, generalmente encontrará algo útil.

¿Dónde está el fichero fuente math.py (socket.py, regex.py, etc.)?

Si no puede encontrar un fichero fuente para un módulo, puede ser un módulo incorporado o cargado dinámicamente implementado en C, C++ u otro lenguaje compilado. En este caso puede no disponer del fichero fuente o puede ser algo como mathmodule.c, en algún lugar de un directorio fuente C (fuera del Python Path).

Hay (al menos) tres tipos de módulos en Python:

  1. módulos escritos en Python (.py);

  2. módulos escritos en C y cargados dinámicamente (.dll, .pyd, .so, .sl, etc.);

  3. módulos escritos en C y enlazados con el intérprete; para obtener una lista de estos, escriba:

    import sys
    print(sys.builtin_module_names)
    

¿Cómo hago ejecutable un script Python en Unix?

Necesita hacer dos cosas: el modo del fichero del script debe ser ejecutable y la primera línea debe comenzar con #! seguido de la ruta al intérprete de Python.

Lo primero se hace ejecutando chmod +x scriptfile o bien chmod 755 scriptfile.

Lo segundo se puede hacer de distintas maneras. La manera más directa es escribir

#!/usr/local/bin/python

en la primera línea de su fichero, usando la ruta donde está instalado el intérprete de Python en su plataforma.

Si quiere que el script sea independiente de donde se ubique el intérprete de Python, puede usar el programa env. Casi todas las variantes de Unix soportan lo siguiente, asumiendo que el intérprete de Python está en un directorio del PATH de usuario:

#!/usr/bin/env python

No haga esto para scripts CGI. La variable PATH para scripts CGI es mínima, así que necesita usar la ruta real absoluta al intérprete.

Ocasionalmente, un entorno de usuario está tan lleno que el programa /usr/bin/env falla; o bien no existe el programa env. En ese caso, puede intentar el siguiente truco (gracias a Alex Rezinsky):

#! /bin/sh
""":"
exec python $0 ${1+"$@"}
"""

Una pequeña desventaja es que esto define el __doc__ del script. Sin embargo, puede arreglarlo añadiendo

__doc__ = """...Whatever..."""

¿Hay un paquete curses/termcap para Python?

Para variantes Unix: La distribución estándar de Python viene con un módulo curses en el subdirectorio Modules, aunque no está compilado por defecto. (Nótese que esto no está disponible en la distribución Windows — no hay módulo curses para Windows.)

El módulo curses soporta características básicas de cursores así como muchas funciones adicionales de ncurses y cursores SYSV como color, soporte para conjuntos de caracteres alternativos, pads, y soporte para ratón. Esto significa que el módulo no es compatible con sistemas operativos que sólo tienen cursores BSD, pero no parece que ningún sistema operativo actualmente mantenido caiga dentro de esta categoría.

Para Windows: use el módulo consolelib.

¿Hay un equivalente en Python al onexit() de C?

El módulo atexit proporciona una función de registro que es similar a onexit() de C.

¿Por qué no funcionan mis manejadores de señales?

El problema más común es que el manejador de señales esté declarado con la lista incorrecta de argumentos. Se llama como

handler(signum, frame)

así que debería declararse con dos argumentos:

def handler(signum, frame):
    ...

Tareas comunes

¿Cómo pruebo un programa o un componente Python?

Python viene con dos frameworks de testing. El módulo doctest encuentra ejemplos en los docstrings para un módulo y los ejecuta, comparando la salida con la salida esperada especificada en la cadena de documentación.

El módulo unittest es un framework de testing más agradable y modelado sobre los frameworks de testing de Java y Smalltalk.

Para hacer más fácil el testing, debería usar un buen diseño modular en su programa. Su programa debería tener casi toda la funcionalidad encapsulada en funciones o en métodos de clases — y esto algunas veces tiene el efecto sorprendente y encantador de que su programa funcione más rápido (porque los accesos a las variables locales son más rápidas que los accesos a las variables globales). Además el programa debería evitar depender de la mutación de variables globales, ya que esto dificulta mucho más hacer el testing.

La «lógica global principal» de su programa puede ser tan simple como

if __name__ == "__main__":
    main_logic()

al final del módulo principal de su programa.

Una vez que su programa esté organizado en una colección manejable de funciones y comportamientos de clases, usted debería escribir funciones de comprobación que ejerciten los comportamientos. Se puede asociar un conjunto de pruebas a cada módulo. Esto suena a mucho trabajo, pero gracias a que Python es tan conciso y flexible, se hace sorprendentemente fácil. Puede codificar de manera mucho más agradable y divertida escribiendo funciones de comprobación en paralelo con el «código de producción», ya que esto facilita encontrar antes errores e incluso fallos de diseño.

Los «módulos de soporte» que no tienen la intención de estar en el módulo principal de un programa pueden incluir un auto test del módulo.

if __name__ == "__main__":
    self_test()

Incluso los programas que interactúan con interfaces externas complejas se pueden comprobar cuando las interfaces externas no están disponibles usando interfaces «simuladas» implementadas en Python.

¿Cómo creo documentación a partir de los docstrings?

El módulo pydoc puede crear HTML desde los docstrings existentes en su código fuente Python. Una alternativa para crear documentación API estrictamente desde docstrings es epydoc. Sphinx también puede incluir contenido docstring.

¿Cómo consigo presionar una única tecla cada vez?

Para variantes Unix hay varias soluciones. Lo más directo es hacerlo usando cursores, pero curses es un módulo bastante amplio para aprenderlo.

Hilos

¿Cómo programo usando hilos?

Asegúrese de usar el módulo threading y no el módulo _thread. El módulo threading construye abstracciones convenientes sobre las primitivas de bajo nivel proporcionadas por el módulo _thread.

Aahz tiene un conjunto de transparencias en su tutorial de hilos que resulta útil: vea http://www.pythoncraft.com/OSCON2001/.

Ninguno de mis hilos parece funcionar: ¿por qué?

Tan pronto como el hilo principal termine, se matan todos los hilos. Su hilo principal está corriendo demasiado rápido, sin dar tiempo a los hilos para hacer algún trabajo.

Una solución sencilla es añadir un sleep al final del programa que sea suficientemente largo para que todos los hilos terminen:

import threading, time

def thread_task(name, n):
    for i in range(n):
        print(name, i)

for i in range(10):
    T = threading.Thread(target=thread_task, args=(str(i), i))
    T.start()

time.sleep(10)  # <---------------------------!

Por ahora (en muchas plataformas) los hilos no corren en paralelo, sino que parece que corren secuencialmente, ¡uno a la vez! La razón es que el planificador de hilos del sistema operativo no inicia un nuevo hilo hasta que el hilo anterior está bloqueado.

Una solución sencilla es añadir un pequeño sleep al comienzo de la función run:

def thread_task(name, n):
    time.sleep(0.001)  # <--------------------!
    for i in range(n):
        print(name, i)

for i in range(10):
    T = threading.Thread(target=thread_task, args=(str(i), i))
    T.start()

time.sleep(10)

En vez de intentar adivinar un valor de retardo adecuado para time.sleep(), es mejor usar algún tipo de mecanismo de semáforo. Una idea es usar el módulo queue para crear un objeto cola, permitiendo que cada hilo añada un token a la cola cuando termine, y permitiendo al hilo principal leer tantos tokens de la cola como hilos haya.

¿Cómo puedo dividir trabajo entre un grupo de hilos?

La manera más fácil es usar el nuevo módulo concurrent.futures, especialmente la clase ThreadPoolExecutor.

O, si quiere tener un control más preciso sobre el algoritmo de despacho, puede escribir su propia lógica manualmente. Use el módulo queue para crear una cola que contenga una lista de trabajos. La clase Queue mantiene una lista de objetos y tiene un método .put(obj) que añade elementos a la cola y un método .get() que los retorna. Esta clase se encargará de los bloqueos necesarios para asegurar que cada trabajo se reparte exactamente una vez.

Aquí hay un ejemplo trivial:

import threading, queue, time

# The worker thread gets jobs off the queue.  When the queue is empty, it
# assumes there will be no more work and exits.
# (Realistically workers will run until terminated.)
def worker():
    print('Running worker')
    time.sleep(0.1)
    while True:
        try:
            arg = q.get(block=False)
        except queue.Empty:
            print('Worker', threading.currentThread(), end=' ')
            print('queue empty')
            break
        else:
            print('Worker', threading.currentThread(), end=' ')
            print('running with argument', arg)
            time.sleep(0.5)

# Create queue
q = queue.Queue()

# Start a pool of 5 workers
for i in range(5):
    t = threading.Thread(target=worker, name='worker %i' % (i+1))
    t.start()

# Begin adding work to the queue
for i in range(50):
    q.put(i)

# Give threads time to run
print('Main thread sleeping')
time.sleep(5)

Cuando se ejecute, esto producirá la siguiente salida:

Running worker
Running worker
Running worker
Running worker
Running worker
Main thread sleeping
Worker <Thread(worker 1, started 130283832797456)> running with argument 0
Worker <Thread(worker 2, started 130283824404752)> running with argument 1
Worker <Thread(worker 3, started 130283816012048)> running with argument 2
Worker <Thread(worker 4, started 130283807619344)> running with argument 3
Worker <Thread(worker 5, started 130283799226640)> running with argument 4
Worker <Thread(worker 1, started 130283832797456)> running with argument 5
...

Consulte la documentación del módulo para más detalles; la clase Queue proporciona una interfaz llena de características.

¿Qué tipos de mutación de valores globales son thread-safe?

Un global interpreter lock (GIL) se usa internamente para asegurar que sólo un hilo corre a la vez en la VM de Python. En general, Python ofrece cambiar entre hilos sólo en instrucciones bytecode; la frecuencia con la que cambia se puede fijar vía sys.setswitchinterval(). Cada instrucción bytecode y por lo tanto, toda la implementación de código C alcanzada por cada instrucción, es atómica desde el punto de vista de un programa Python.

En teoría, esto significa que un informe exacto requiere de un conocimiento exacto de la implementación en bytecode de la PVM. En la práctica, esto significa que las operaciones entre variables compartidas de tipos de datos built-in (enteros, listas, diccionarios, etc.) que «parecen atómicas» realmente lo son.

Por ejemplo, las siguientes operaciones son todas atómicas (L, L1, L2 son listas, D, D1, D2 son diccionarios, x, y son objetos, i, j son enteros):

L.append(x)
L1.extend(L2)
x = L[i]
x = L.pop()
L1[i:j] = L2
L.sort()
x = y
x.field = y
D[x] = y
D1.update(D2)
D.keys()

Estas no lo son:

i = i+1
L.append(L[-1])
L[i] = L[j]
D[x] = D[x] + 1

Las operaciones que reemplazan otros objetos pueden invocar el método __del__() de esos otros objetos cuando su número de referencias alcance cero, y eso puede afectar a otras cosas. Esto es especialmente cierto para las actualizaciones en masa de diccionarios y listas. Cuando se esté en duda, ¡use un mutex!

¿Podemos deshacernos del Global Interpreter Lock?

El global interpreter lock (GIL) se percibe a menudo como un obstáculo en el despliegue de Python sobre máquinas servidoras finales de múltiples procesadores, porque un programa Python multihilo efectivamente sólo usa una CPU, debido a la exigencia de que (casi) todo el código Python sólo puede correr mientras el GIL esté activado.

En los días de Python 1.5, Greg Stein de hecho implementó un conjunto amplio de parches (los parches «libres de hilo») que eliminaba el GIL y lo reemplazaba con un bloqueo de grano fino. Adam Olsen hizo recientemente un experimento similar con su proyecto python-safethread. Desafortunadamente, ambos experimentos mostraron una aguda caída en el rendimiento (al menos del 30% o más baja), debido a la cantidad de bloqueos de grano fino necesarios para compensar la eliminación del GIL.

¡Esto no significa que no pueda hacer buen uso de Python en máquinas de múltiples CPU! Usted sólo tiene que ser creativo a la hora de dividir el trabajo entre múltiples procesos en vez de entre múltiples hilos. La clase ProcessPoolExecutor del nuevo módulo concurrent.futures proporciona una manera sencilla de hacer esto; el módulo multiprocessing proporciona una API de bajo nivel en caso de que se quiera tener un mayor control sobre el despacho de las tareas.

El uso sensato de extensiones C también ayudará; si usa una extensión C para ejecutar una tarea que consume mucho tiempo, la extensión puede liberar al GIL mientras el hilo de ejecución esté en el código C y permite a otros hilos hacer trabajo. Algunos módulos de la biblioteca estándar tales como zlib y hashlib ya lo hacen.

Se ha sugerido que el GIL debería ser un bloqueo por estado de intérprete, en vez de realmente global; luego los intérpretes no serían capaces de compartir objetos. Desafortunadamente, esto tampoco es probable que ocurra. Sería una tremenda cantidad de trabajo, porque muchas implementaciones de objetos actualmente tienen un estado global. Por ejemplo, los enteros pequeños y las cadenas pequeñas están cacheadas; estas caches se tendrían que mover al estado del intérprete. Otros tipos de objetos tienen su propia lista libre; estas listas libres se tendrían que mover al estado del intérprete. Y así sucesivamente.

Y dudo de si se puede hacer en tiempo finito, porque el mismo problema existe para extensiones de terceros. Es probable que las extensiones de terceros se escriban más rápido de lo que se puedan convertir para almacenar todo su estado global en el estado del intérprete.

Y finalmente, una vez que tenga múltiples intérpretes sin compartir ningún estado, ¿qué habrá ganado sobre correr cada intérprete en un proceso separado?

Entrada y Salida

¿Cómo borro un fichero? (Y otras preguntas sobre ficheros…)

Use os.remove(filename) o os.unlink(filename); para la documentación, vea el módulo os. Las dos funciones son idénticas; unlink() es simplemente el nombre de la llamada al sistema UNIX para esta función.

Para borrar un directorio, use os.rmdir(); use os.mkdir() para crear uno. os.makedirs(path) creará cualquier directorio intermedio que no exista en path. os.removedirs(path) borrará los directorios intermedios siempre y cuando estén vacíos; si quiere borrar un árbol de directorios completo y sus contenidos, use shutil.rmtree().

Para renombrar un fichero, use os.rename(old_path, new_path).

Para truncar un fichero, ábralo usando f = open(filename, "rb+"), y use f.truncate(offset); el desplazamiento toma por defecto la posición actual de búsqueda. También existe os.ftruncate(fd, offset) para ficheros abiertos con os.open(), donde fd es el descriptor del fichero (un entero pequeño).

El módulo shutil también contiene distintas funciones para trabajar con ficheros incluyendo copyfile(), copytree() y rmtree().

¿Cómo copio un fichero?

The shutil module contains a copyfile() function. Note that on MacOS 9 it doesn’t copy the resource fork and Finder info.

¿Cómo leo (o escribo) datos binarios?

Para leer o escribir formatos binarios de datos complejos, es mejor usar el módulo struct. Esto le permite tomar una cadena de texto que contiene datos binarios (normalmente números) y convertirla a objetos de Python; y viceversa.

Por ejemplo, el siguiente código lee de un fichero dos enteros de 2-bytes y uno de 4-bytes en formato big-endian:

import struct

with open(filename, "rb") as f:
    s = f.read(8)
    x, y, z = struct.unpack(">hhl", s)

El “>” en la cadena de formato fuerza los datos a big-endian; la letra “h” lee un «entero corto» (2 bytes), y “l” lee un «entero largo» (4 bytes) desde la cadena de texto.

Para datos que son más regulares (por ejemplo una lista homogénea de enteros o flotantes), puede también usar el módulo array.

Nota

Para leer y escribir datos binarios, es obligatorio abrir el fichero en modo binario (aquí, pasando "rb" a open()). Si, en cambio, usa "r" (por defecto), el fichero se abrirá en modo texto y f.read() retornará objetos str en vez de objetos bytes.

No consigo usar os.read() en un pipe creado con os.popen(); ¿por qué?

os.read() es una función de bajo nivel que recibe un descriptor de fichero, un entero pequeño representando el fichero abierto. os.popen() crea un objeto fichero de alto nivel, el mismo tipo que retorna la función built-in open(). Así, para leer n bytes de un pipe p creado con os.popen(), necesita usar p.read(n).

¿Cómo accedo al puerto serial (RS232)?

Para Win32, POSIX (Linux, BSD, etc.), Jython:

Para Unix, vea una publicación Usenet de Mitch Chapman:

¿Por qué al cerrar sys.stdout (stdin, stderr) realmente no se cierran?

Los objetos de tipo fichero en Python son una capa de abstracción de alto nivel sobre los descriptores de ficheros de bajo nivel de C.

Para la mayoría de objetos de tipo fichero que cree en Python vía la función built-in open(), f.close() marca el objeto de tipo fichero Python como ya cerrado desde el punto de vista de Python, y también ordena el cierre del descriptor de fichero subyacente en C. Esto además ocurre automáticamente en el destructor de f, cuando f se convierte en basura.

Pero stdin, stdout y stderr se tratan de manera especial en Python, debido a un estatus especial que también tienen en C. Ejecutando sys.stdout.close() marca el objeto fichero de nivel Python para ser cerrado, pero no cierra el descriptor de fichero asociado en C.

Para cerrar el descriptor de fichero subyacente en C para uno de estos tres casos, debería primero asegurarse de que eso es realmente lo que quiere hacer (por ejemplo, puede confundir módulos de extensión intentado hacer I/O). Si es así, use os.close():

os.close(stdin.fileno())
os.close(stdout.fileno())
os.close(stderr.fileno())

O puede usar las constantes numéricas 0, 1 y 2, respectivamente.

Programación de Redes/Internet

¿Qué herramientas de Python existen para WWW?

Vea los capítulos titulados Protocolos y soporte de Internet y Manejo de Datos de Internet en el manual de referencia de bibliotecas. Python tiene muchos módulos que le ayudarán a construir sistemas web del lado del servidor y del lado del cliente.

Paul Boddie mantiene un resumen de los frameworks disponibles en https://wiki.python.org/moin/WebProgramming.

Cameron Laird mantiene un conjunto útil de páginas sobre tecnologías web Python en http://phaseit.net/claird/comp.lang.python/web_python.

¿Cómo puedo imitar un envío de formulario CGI (METHOD=POST)?

Me gustaría recuperar páginas web que son resultado del envío de un formulario. ¿Existe algún código que me permita hacer esto fácilmente?

Sí. Aquí hay un ejemplo sencillo que usa urllib.request:

#!/usr/local/bin/python

import urllib.request

# build the query string
qs = "First=Josephine&MI=Q&Last=Public"

# connect and send the server a path
req = urllib.request.urlopen('http://www.some-server.out-there'
                             '/cgi-bin/some-cgi-script', data=qs)
with req:
    msg, hdrs = req.read(), req.info()

Nótese que para operaciones POST de tipo percent-encoded, las cadenas de consulta tienen que estar entrecomilladas usando urllib.parse.urlencode(). Por ejemplo, para enviar name=Guy Steele, Jr.:

>>> import urllib.parse
>>> urllib.parse.urlencode({'name': 'Guy Steele, Jr.'})
'name=Guy+Steele%2C+Jr.'

Ver también

HOWTO - Cómo obtener recursos de Internet con el paquete urllib para ejemplos más detallados.

¿Qué modulo debería usar para generación de HTML?

Puede encontrar una colección de enlaces útiles en la página wiki de programación web.

¿Cómo envío correo desde un script Python?

Use el módulo smtplib de la biblioteca estándar.

Aquí hay un remitente simple interactivo que lo usa. Este método trabajará en cualquier máquina que soporte un listener SMTP.

import sys, smtplib

fromaddr = input("From: ")
toaddrs  = input("To: ").split(',')
print("Enter message, end with ^D:")
msg = ''
while True:
    line = sys.stdin.readline()
    if not line:
        break
    msg += line

# The actual mail send
server = smtplib.SMTP('localhost')
server.sendmail(fromaddr, toaddrs, msg)
server.quit()

Una alternativa sólo para UNIX es usar sendmail. La ubicación del programa sendmail varía entre sistemas; algunas veces está en /usr/lib/sendmail, otras veces en /usr/sbin/sendmail. El manual de sendmail le ayudará. Aquí hay un ejemplo de código:

import os

SENDMAIL = "/usr/sbin/sendmail"  # sendmail location
p = os.popen("%s -t -i" % SENDMAIL, "w")
p.write("To: receiver@example.com\n")
p.write("Subject: test\n")
p.write("\n")  # blank line separating headers from body
p.write("Some text\n")
p.write("some more text\n")
sts = p.close()
if sts != 0:
    print("Sendmail exit status", sts)

¿Cómo evito el bloqueo en el método connect() de un socket?

El módulo select es mayoritariamente usado para ayudar con entrada/salida de sockets.

Para prevenir el bloqueo en la conexión TCP, puede establecer el socket en modo no bloqueante. Luego cuando ejecute connect(), conectará inmediatamente (improbable) u obtendrá una excepción que contiene el número de error como .errno. errno.EINPROGRESS indica que la conexión está en curso, pero aún no ha terminado. Diferentes sistemas operativos retornarán diferentes valores, así que debe comprobar cuál es el retornado en su sistema.

Puede usar el método connect_ex() para evitar crear una excepción. Retornará simplemente el número de error. Para sondear, puede llamar más tarde a connect_ex() de nuevo – 0 o errno.EISCONN indican que está conectado – o puede pasar este socket a select para comprobar si se puede escribir en él.

Nota

El módulo asyncore ofrece una enfoque de tipo framework al problema de escribir código de red no bloqueante. La biblioteca de terceros Twisted es una alternativa popular y ofrece muchas capacidades.

Bases de datos

¿Hay paquetes para interfaces a bases de datos en Python?

Sí.

Interfaces a hashes basados en disco tales como DBM y GDBM están también incluidas en Python estándar. También hay un módulo sqlite3, que proporciona una base de datos relacional ligera basada en disco.

Está disponible el soporte para la mayoría de bases de datos relacionales. Vea la página wiki de Programación de Bases de datos para más detalles.

¿Cómo implementar objetos persistentes en Python?

El módulo de biblioteca pickle soluciona esto de una forma muy general (aunque todavía no puede almacenar cosas como ficheros abiertos, sockets o ventanas), y el módulo de biblioteca shelve usa pickle y (g)dbm para crear mapeos persistentes que contienen objetos arbitrarios Python.

Matemáticas y Numérica

¿Cómo genero números aleatorios en Python?

El módulo estándar random implementa un generador de números aleatorios. El uso es simple:

import random
random.random()

Esto retorna un número flotante aleatorio en el rango [0, 1).

Hay también muchos otros generadores especializados en este módulo, tales como:

  • randrange(a, b) selecciona un entero en el rango [a, b).

  • uniform(a, b) selecciona un número flotante en el rango [a, b).

  • normalvariate(mean, sdev) muestrea una distribución normal (Gausiana).

Algunas funciones de alto nivel operan directamente sobre secuencias, tales como:

  • choice(S) selecciona un elemento aleatorio de una secuencia dada

  • shuffle(L) reorganiza una lista in-situ, es decir, la permuta aleatoriamente

También hay una clase Random que usted puede instanciar para crear múltiples generadores independientes de valores aleatorios.