# Preguntas frecuentes sobre bibliotecas y extensiones

## 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 paquetes de terceros, busque Python Package Index o pruebe Google u otro motor de búsqueda web. La búsqueda de «Python» más una palabra clave o dos para su 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.

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

The `atexit` module provides a register function that is similar to C’s `onexit()`.

### ¿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`.

### 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 el módulo `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.current_thread(), end=' ')
                print('queue empty')
                break
            else:
                print('Worker', threading.current_thread(), 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

Operations that replace other objects may invoke those other objects” `__del__()` method when their reference count reaches zero, and that can affect things. This is especially true for the mass updates to dictionaries and lists. When in doubt, use a 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?

El módulo `shutil` contiene una función `copyfile()`. Nótese que en volúmenes de Windows NTFS, no copia los flujos alternativos de datos (ADS) ni las bifurcaciones de recursos (resource forks) en los volúmenes macOS HFS+, aunque ahora ambos rara vez son utilizados. Además tampoco copia los permisos ni metadatos de los archivos, pero si se usa `shutil.copy2()` en su lugar, se preservará la mayor parte (aunque no todo).

### ¿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, OSX, Linux, BSD, Jython, IronPython:

> https://pypi.org/project/pyserial/

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

> https://groups.google.com/groups?selm=34A04430.CF9@ohioee.com

### ¿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 https://web.archive.org/web/20210224183619/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.

To prevent the TCP connect from blocking, you can set the socket to non-blocking mode. Then when you do the `connect()`, you will either connect immediately (unlikely) or get an exception that contains the error number as `.errno`. `errno.EINPROGRESS` indicates that the connection is in progress, but hasn’t finished yet. Different OSes will return different values, so you’re going to have to check what’s returned on your system.

You can use the `connect_ex()` method to avoid creating an exception. It will just return the errno value. To poll, you can call `connect_ex()` again later – `0` or `errno.EISCONN` indicate that you’re connected – or you can pass this socket to `select.select()` to check if it’s writable.

Nota

The `asyncio` module provides a general purpose single-threaded and concurrent asynchronous library, which can be used for writing non-blocking network code. The third-party Twisted library is a popular and feature-rich alternative.

## 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.