Apartados


1. Tu primer programa en Python

Nivel de dificultad:1 sobre 5

``No entierres tu carga en un santo silencio.
¿Tienes un problema? Estupendo. Alégrate,
sumérgete en él e investiga.''
--Ven. Henepola Gunarata

1.1 Inmersión

Los libros sobre programación suelen comenzar con varios capítulos sobre los fundamentos y van, poco a poco, avanzando hasta llegar a hacer programas útiles. Vamos a saltarnos todo eso. Lo primero que vamos a ver es un programa Python completo. Probablemente no tenga ningún sentido para ti. No te preocupes por eso, vamos a diseccionarlo línea por línea. Primero léelo y trata de interpretarlo.

# parahumanos.py

SUFIJOS = 1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], 1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']

def tamanyo_aproximado(tamanyo, un_kilobyte_es_1024_bytes=True): '''Convierte un tamaño de fichero en formato legible por personas

Argumentos/parámetros: tamanyo - tamaño de fichero en bytes un_kilobyte_es_1024_bytes - si True (por defecto), usa múltiplos de 1024 si False, usa múltiplos de 1000

retorna: string

''' if tamanyo < 0: raise ValueError('el número debe ser no negativo')

multiplo = 1024 if un_kilobyte_es_1024_bytes else 1000 for sufijo in SUFIJOS[multiplo]: tamanyo /= multiplo if tamanyo < multiplo: return '0:.1f 1'.format(tamanyo, sufijo)

raise ValueError('número demasiado grande')

if __name__ == '__main__': print(tamanyo_aproximado(1000000000000, False)) print(tamanyo_aproximado(1000000000000))

Antes de analizarlo paso a paso vamos a ejecutar el programa en la línea de comandos. En Linux o en Mac debes teclear: python3 parahumanos.py1.1. El resultado será parecido a lo siguiente:

tu_usuario@tu_ordenador: /inmersionEnPython3$ python3 parahumanos.py
1.0 TB
931.3 GiB

En Windows debes teclear lo mismo: python3 parahumanos.py, únicamente varía la forma del prompt de la consola. El resultado será parecido a:

C:

inmersionenpython3:> python3 parahumanos.py 1.0 TB 931.3 GiB

¿Qué ha pasado? Acabas de ejecutar tu primer programa Python. Has ejecutado el intérprete de Python en la línea de comandos (python3), y le has pasado como parámetro el nombre del fichero de script (parahumanos.py) que querías ejecutar.

El fichero de script, a su vez, define una única función de python, la función tamnyo_aproximado, que toma como parámetros un tamaño de fichero con una precisión de bytes y calcula el tamaño en una unidad mayor en la que el valor quede más bonito, a cambio, el resultado es aproximado. (El funcionamiento del Explorador de Windows; del Finder de Mac OS X, o de Nautilus, Dolphin o Thunar de Linux es muy parecido. Si muestras en cualquiera de ellos una carpeta de documentos en modo detalle, de forma que se vean en diferentes columnas, el icono del documento, nombre, tamaño, tipo, fecha de última modificación, etc. Observarás que si un documento determinado ocupa 1093 bytes, en la columna de tamaño no dirá eso, sino que dirá algo así como 1 KB. Esto es lo que hace la función tamanyo_aproximado)

Las líneas de código print(tamanyo_aproximado(argumentos)) del final del script, líneas 31 y 32, son dos llamadas a funciones --primero se llama a la función tamanyo_aproximado() pasándole unos parámetros (también llamados argumentos), esta función se ejecuta y devuelve un resultado que, posteriormente, se pasa como parámetro a la función print(). Todo ello en la misma línea.

La función print() es interna del lenguaje Python1.2; nunca verás una declaración explícita de ella. La puedes usar cuando quieras, en cualquier parte de un programa Python1.3.

¿Porqué la ejecución del script en la línea de comandos retorna siempre la misma respuesta? Lo veremos más adelante. Primero vamos a ver el funcionamiento de la función tamanyo_aproximado().

1.2 Declaración de funciones

Python dispone de funciones como la mayoría de los lenguajes, pero no tiene ficheros de cabecera como c++ o secciones de interface/implementation como en Pascal. En Python únicamente hay que declarar la función, como en el siguiente ejemplo:

def tamanyo_aproximado(tamanyo, un_kilobyte_es_1024_bytes=True):

La palabra reservada def inicia la declaración de la función, seguida del nombre que le quieres dar a la misma, seguida de los parámetros de la función entre paréntesis. Separándolos por comas en caso de que sean varios parámetros.

En Python cuando necesitas una función, solamente tienes que declararla.

Observa también que, en Python, las funciones no definen un tipo de datos de retorno. No se especifica el tipo de datos del valor que retornan las funciones. Es más, ni siquiera se especifica si se retorna o no un valor.

En realidad, todas las funciones de Python tienen un valor de retorno; si dentro del código de la función se ejecuta una sentencia return, el valor que acompaña a la sentencia será el valor de retorno, en caso contrario se retorna el valor None, que es la forma de expresar el vacío (null) en Python.

En algunos lenguajes, las funciones que retornan un valor se declaran con la palabra function, y las subrutinas que no retornan un valor con la palabra sub. En Python no existen las subrutinas. Todas son funciones, todas las funciones devuelven un valor (None si tú no devuelves algo expresamente con la palabra reservada return) y todas las funciones comienzan con la palabra def.

La función tamanyo_aproximado() recibe dos parámetros o argumentos, --tamanyo y un_kilobyte_es_1024_bytes-- pero ninguno de ellos especifica un tipo de datos. En Python, las variables nunca se tipifican explícitamente, Python deduce y mantiene el tipo de datos de la variable de forma interna según el valor que tenga asignado la misma.

En Java y otros lenguajes con tipificación estática, debes especificar el tipo de datos de los parámetros y valor de retorno de cada función. En Python nunca especificas el tipo de datos de nada de forma explícita. Python mantiene el rastro de los tipos de datos de forma interna basándose en los valores que asignes a las variables.

1.2.1 Parámetros opcionales y con nombre

Python permite que los parámetros de una función tengan valores por defecto; si la función se llama (para ejecutarla) si indicar el parámetro Python usará el valor por defecto para asignarlo al parámetro que no se ha especificado en la llamada a la función. Asimismo, los parámetros se pueden pasar en la llamada en cualquier orden si se utilizan parámetros con nombre.

Veamos de nuevo la declaración de la función tamanyo_aproximado().

def tamanyo_aproximado(tamanyo, un_kilobyte_es_1024_bytes=True):

El segundo parámetro un_kilobyte_es_1024_bytes, especifica un valor por defecto igual a True. Como consecuencia, este parámetro pasa a ser opcional; puedes llamar a la función sin pasarlo en los paréntesis. Python se comportará como si lo hubieras llamado con el valor True como segundo parámetro.

Veamos el final del script1.4:

if __name__ == '__main__':
    print(tamanyo_aproximado(1000000000000, False))
    print(tamanyo_aproximado(1000000000000))

  1. La primera llamada a la función (línea 2) utiliza dos parámetros. Durante la ejecución de la función tamanyo_aproximado un_kilobyte_es_1024_bytes tendrá el valor False, que es lo que se pasa como segundo parámetro en la llamada a la función.

  2. La segunda llamada a la función (línea 3) utiliza un único parámetro. Pero Python no se queja ya que el segundo es opcional. Como no se especifica, el segundo parámetro utiliza su valor por defecto True, de acuerdo a lo que se definió en la declaración de la función.

También puedes pasar los valores a una función utilizando nombres. Prueba lo siguiente en la consola:

»> from parahumanos import tamanyo_aproximado
»> tamanyo_aproximado(4000, un_kilobyte_es_1024_bytes=False)
'4.0 KB'
»> tamanyo_aproximado(tamanyo=4000, un_kilobyte_es_1024_bytes=False)
'4.0 KB'
»> tamanyo_aproximado(un_kilobyte_es_1024_bytes=False, tamanyo=4000)
'4.0 KB'
»> tamanyo_aproximado(un_kilobyte_es_1024_bytes=False, 4000)
SyntaxError: non-keyword arg after keyword arg (<pyshell#4>, line 1)
»> tamanyo_aproximado(tamanyo=4000, False)
SyntaxError: non-keyword arg after keyword arg (<pyshell#5>, line 1)
»> 

  1. Línea 2: Llama a la función tamanyo_aproximado() pasándole 4000 al primer parámetro (tamanyo) y el valor False en el denominado un_kilobyte_es_1204_bytes (En este caso coincide que el parámetro con nombre se está pasando en la segunda posición y también está declarado en la función como segundo parámetro, pero esto es simplemente una coincidencia).

  2. Línea 4: Llama a la función tamanyo_aproximado() pasándole 4000 al parámetro denominado tamanyo y False al parámetro denominado un_kilobyte_es_1024_bytes (Estos parámetros coinciden en orden con los de la declaración de la función, pero vuelve a ser una simple coincidencia).

  3. Línea 6: Llama a a la función tamanyo_aproximado() pasándole False al parámetro denominado un_kilobyte_es_1024_bytes y 4000 al parámetro denominado tamanyo (Esta es la utilidad de usar nombres en las llamadas a una función, poder pasarlos en cualquier orden, e incluso no pasar alguno de los existentes para que tomen valores por defecto mientras sí que pasas uno de los últimos parámetros de la función).

  4. Línea 8: Esta llamada a la función falla porque se usa un parámetro con nombre seguido de uno sin nombre (por posición). Esta forma de llamar a la función siempre falla. Python lee la lista de parámetros de izquierda a derecha, en cuanto aparece un parámetro con nombre, el resto de parámetros debe también proporcionarse por nombre. Los primeros pueden ser por posición.

  5. Línea 10: Esta llamada también falla por la misma razón que la anterior. ¿Te sorprende? Después de todo, el primer parámetro se ha denominado tamanyo y recibe el valor 4000, es obvio que el valor False debería asignarse al parámetro un_kilobyte_es_1024_bytes. Pero Python no funciona de esa forma. Tan pronto como lee un parámetro con nombre, todos los parámetros siguientes (a la derecha) tienen que llevar el nombre del parámetro.

1.3 Cómo escribir código legible

No te voy a aburrir con una larga charla sobre la importancia de documentar el código. Solamente decir que el código se escribe una vez pero se lee muchas veces, y que quien más lo va a leer eres tú, seis meses después de haberlo escrito (por ejemplo: cuando ya no te acuerdes de nada pero necesites corregir o añadir algo). Python hace fácil escribir código legible, aprovéchate de ello. Me lo agradecerás dentro de seis meses.

1.3.1 Cadenas de texto de documentación

Puedes documentar una función proporcionándole una cadena de documentación (abreviando se suele hablar de docstring). En este programa, la función tamanyo_aproximado() tiene una cadena de documentación (docstring):

def tamanyo_aproximado(tamanyo, un_kilobyte_es_1024_bytes=True):
    '''Convierte un tamaño de fichero en formato legible por personas

Argumentos/parámetros: tamanyo - tamaño de fichero en bytes un_kilobyte_es_1024_bytes - si True (por defecto), usa múltiplos de 1024 si False, usa múltiplos de 1000

retorna: string

'''

La comillas triples sirven para escribir cadenas de texto que ocupen más de una línea. Todo lo que se escribe entre las comillas triples forma parte de una única cadena de texto, incluidos los espacios en blanco, retornos de carro, saltos de línea y otras comillas sueltas. Este tipo de cadenas de texto lo puedes utilizar donde quieras dentro del código Python, pero normalmente se utilizan para definir docstring (cadenas de texto de documentación).

Las comillas triples son la manera más simple de escribir cadenas de texto que incluyan, a su vez, comillas simples y/o dobles, como cuando en Perl 5 se utiliza q/.../

Todas las funciones se merecen un docstring que las explique

En este ejemplo, todo lo que se encuentra entre las comillas triples es el docstring de la función, que sirve para documentar lo que hace la función. Un docstring, si existe, debe ser lo primero que aparece definido en una función (es decir, se debe encontrar en la primera línea que aparece después de la declaración de la función). Técnicamente no necesitas escribir un docstring para cada función, pero deberías. Sé que lo has escuchado en las clases que programación a las que hayas asistido, pero Python te da un incentivo mayor para que lo hagas: los docstring están disponibles en tiempo de ejecución como un atributo de la función.

Muchos entornos integrados de programación (IDEs) utilizan los docstring para proporcionar ayuda y documentación sensible al contexto, de forma que cuando teclees el nombre de una función, aparece su docstring como pista sobre el significado de la función y de sus parámetros. Esto puede ser muy útil, tan útil como explicativos sean los docstring que escribas.


1.4 El camino de búsqueda para import

Antes de continuar, quiero mencionar brevemente el camino1.5 de búsqueda de las librerías. Cuando importas un módulo, Python busca en varios lugares hasta encontrarlo. En concreto, busca en todos los directorios que se encuentren definidos en la variable sys.path. Como se trata de una lista, puedes verla fácilmente o modificarla con los métodos estándares de manipulación de listas. (Aprenderás a trabajar con listas en el capítulo 2 sobre Tipos de Dato Nativos).

»> import sys
»> sys.path
['', 
 '/usr/lib/python3.0',  
 '/usr/lib/python3.0/plat-linux2',
 '/usr/lib/python3.0/lib-dynload',
 '/usr/lib/python3.0/dist-packages',
 '/usr/local/lib/python3.0/dist-packages']
»> sys
<module 'sys' (built-in)>
»> sys.path.insert(0, '/home/jmgaguilera/inmersionenpython3/ejemplos')
»> sys.path
['/home/jmgaguilera/inmersionenpython3/ejemplos',
 '',
 '/usr/lib/python3.0',
 '/usr/lib/python3.0/plat-linux2',
 '/usr/lib/python3.0/lib-dynload',
 '/usr/lib/python3.0/dist-packages',
 '/usr/local/lib/python3.0/dist-packages']
»> 

  1. Línea 1: Al importar el paquete sys de esta forma, todas sus funciones y atributos quedan a disposición del programador para su uso.

  2. Líneas 2-8: sys.path es una variable (path) del paquete sys que contiene una lista de los directorios que constituyen el camino de búsqueda (El tuyo será diferente, ya que depende del sistema operativo, de la versión de Python que tengas instalada, y del lugar en el que está instalada). Siempre que se haga un import en el código, Python buscará en estos directorios (por orden), hasta encontrar un fichero cuyo nombre coincida con el valor que se usa en la sentencia import más la extensión .py.

  3. Líneas 9-10: En realidad te he mentido un poco, la realidad es un poco más compleja, no todos los módulos se almacenan en ficheros con extensión .py. Algunos de ellos, como el módulo sys son módulos internos (built-in); no existen en ficheros, están construidos internamente en el propio lenguaje. En la práctica funcionan exactamente igual que los módulos que están en ficheros, la única diferencia es que no existe el código fuente, ¡Porque no están escritos en Python! (El módulo sys está escrito en lenguaje c).

  4. Línea 11: En tiempo de ejecución puedes añadir un nuevo directorio al camino de búsqueda de Python añadiendo un directorio a la variable sys.path, así Python también buscará en él cada vez que intentes importar un módulo. El efecto de este cambio dura mientras se mantenga en ejecución Python. Al finalizar, y volver a entrar en Python, el camino (la variable sys.path) volverá a tener los valores iniciales.

  5. Líneas 12-19: Al ejecutar sys.path.insert(0, path) se nsertó un nuevo directorio en la primera posición (en Python la primera posición se numera con el cero) de la lista de sys.path. Casi siempre, será esto lo que quieras hacer. En casos en los que exista algún conflicto de nombres (por ejemplo, si Python tiene su propia versión de una librería y es de la versión 2, pero quieres utilizar otra que sea de la versión 3), así te aseguras que tus módulos se encuentran antes y ejecutan en lugar de los originales.

1.5 En Python todo es un Objeto

En caso de te lo hayas perdido, acabo de decir que las funciones de Python tienen atributos, y que esos atributos están disponibles en tiempo de ejecución. Una función, como todo lo demás en Python, es un objeto.

Abre la consola interactiva de Python y ejecuta lo siguiente:

»> import parahumanos
»> print(parahumanos.tamanyo_aproximado(4096, True))
4.0 KiB
»> print(parahumanos.tamanyo_aproximado.__doc__)
Convierte un tamaño de fichero en un formato legible por personas

Argumentos/parámetros: tamanyo - tamaño de fichero en bytes un_kilobyte_es_1024_bytes - si True (por defecto), usa múltiplos de 1024 si False, usa múltiplos de 1000

retorna: string

»>

  1. Línea 1: Importa (carga en memoria) el programa parahumanos como un módulo --un trozo de código que puedes utilizar de forma interactiva o desde un programa Python mayor. Una vez se ha importado el módulo, puedes utilizar (referenciar) cualquiera de sus funciones públicas, clases o atributos. Si desde un módulo se desea utilizar la funcionalidad de otro, basta con hacer exactamente lo mismo que en esta línea de la consola interactiva.

  2. Línea 2: Cuando quieres utilizar las funciones que estén definidas en los módulos importados, tienes que añadir el nombre del módulo. No es posible utilizar simplemente tamanyo_aproximado, debes utilizar parahumanos.tamanyo_aproximado. Si has utilizado Java, esta forma de utilizar una función debería sonarte.

  3. Línea 4: En este caso, en lugar de llamar a la función como podrías esperar, se consulta uno de los atributos de la función, __doc__.

En Python import es equivalente al require de Perl. Cuando importas (import) un módulo de Python puedes acceder a todas sus funciones con la sintaxis módulo.función. En Perl, cuando se requiere (require) un módulo puedes acceder a todas sus funciones con la sintaxis módulo::función

1.5.1 ¿Qué es un objeto?

En Python todo es un objeto, y todos los objetos pueden tener atributos y métodos. Todas las funciones son objetos, y tienen el atributo __doc__, que retorna el docstring que se haya definido en el código fuente. El módulo sys es también un objeto que tiene (entre otras cosas) un atributo denominado path. Como se ha dicho: todo lo que se defina en Python es un objeto y puede tener atributos y métodos.

Sin embargo, no hemos contestado aún a la pregunta fundamental: ¿Qué es un objeto? Los diferentes lenguajes de programación definen objeto de diferente forma. En algunos, significa que todos los objetos deben tener atributos y métodos; en otros, significa que todos los objetos pueden tener subclases. En Python la definición es más relajada. Algunos objetos no tienen ni atributos ni métodos, pero podrían. No todos los objetos pueden tener subclases. Pero todo es un objeto en el sentido de que pueden asignarse a variables y pasarse como parámetro de una función.

Puede que hayas oído en otro contexto de programación el término objeto de primera clase. En Python, las funciones son objetos de primera clase. Puedes pasar una función como parámetro de otra función. Los módulos también son objetos de primera clase. Puedes pasar un módulo completo como parámetro de una función. Las clases son objetos de primera clase, y las instancias de las clases también lo son.

Esto es importante, por lo que lo voy a repetir en caso de que se te escapara las primeras veces: en Python, todo es un objeto. Las cadenas son objetos, las listas son objetos. Las funciones son objetos. Las clases son objetos. Las instancias de las clases son objetos. E incluso los módulos son objetos.

1.6 Indentar código

Las funciones de Python no tienen begin o end, y tampoco existen llaves que marquen donde comienza y acaba el código de una función. El único delimitador es el símbolo de los dos puntos (:) y el propio indentado del código.

def tamanyo_aproximado(tamanyo, un_kilobyte_es_1024_bytes=True):
    if tamanyo < 0:
        raise ValueError('El número debe ser no negativo')

multiplo = 1024 if un_kilobyte_es_1024_bytes else 1000 for sufijo in SUFIJO[multiplo]: tamanyo /= multiplo if tamanyo < multiplo: return '0:.1f 1'.format(tamanyo, sufijo)

raise ValueError('número demasiado grande')

  1. Línea 1: Los bloques de código se definen por su indentado. Por ``bloque de código'' se entiende lo siguiente: funciones, sentencias if, bucles for, bucles while y similar. Al indentar se inicia el bloque y al desindentar se finaliza. No existen llaves, corchetes o palabras clave para iniciar y finalizar un bloque de forma explícita. Esto implica que los espacios en blanco son significativos, y deben ser consistentes. En este ejemplo, el código de la función está indentado con cuatro espacios. No es necesario que sean cuatro, pero sí que sea consistente y siempre sean los mismos. La primera línea que no esté indentada delimita el final de la función.

  2. Línea 2: En Python, la sentencia if debe contener un bloque de código. Si la expresión que sigue al if es verdadera1.6 se ejecuta el bloque indentado que contiene el if, en caso contrario lo que se ejecuta es el bloque contenido en el else (si existe). Observa la ausencia de paréntesis alrededor de la expresión.

  3. Línea 3: Esta línea se encuentra dentro del bloque de código del if. La sentencia raise elevará una excepción (del tipo ValueError, pero únicamente si tamanyo < 0.

  4. Línea 4: Esta línea no marca el final de la función. Las líneas que están completamente en blanco no cuentan. Únicamente sirven para hacer más legible el código, pero no cuentan como delimitadores de código. La función continúa en la línea siguiente.

  5. Línea 6: El bucle for también marca el comienzo de un bloque de código. Los bloques pueden contener múltiples líneas, siempre que estén indentadas con el mismo número de espacios. Este bucle for contiene tres líneas de código en él. No existe ninguna otra sintaxis especial para los bloques de varias líneas. Basta con indentar y... ¡seguir adelante con el trabajo!

Python utiliza los saltos de línea para separar las sentencias y los dos puntos y la indentación para separar los bloques de código. C++ y Java utilizan puntos y coma para separar sentencias y llaves para separar los bloques de código.

Después de algunas protestas iniciales e insidiosas analogías con Fortran, seguro que harás las paces con esta forma de marcar los bloques de código y comenzarás a apreciar sus beneficios. Uno de los mayores beneficios es que todos los programas Python tienen un formato similar, al ser la indentación un requisito del lenguaje y no un elemento de estilo. La consecuencia inmediata es que los programas Python son más fáciles de leer y comprender por parte de una persona diferente de su programador1.7.

1.7 Excepciones

Las excepciones están en todas partes en Python. Prácticamente todos los módulos de la librería estándar las utilizan, y el propio lenguaje las lanza en muchas circunstancias. Las verás una y otra vez a lo largo del libro.

¿Qué es una excepción? Normalmente es un error, una indicación de que algo fue mal (No todas las excepciones son errores, pero no te preocupes por eso ahora). Algunos lenguajes de programación fomentan que se retornen códigos de error en las funciones, que los programadores tendrán que chequear. Python fomenta el uso de las excepciones, que los programadores tienen que capturar y manejar.

Al contrario que Java, las funciones de Python no declaran las excepciones que podrían elevar. Te corresponde a ti determinar las excepciones que pueden suceder y necesitas capturar.

Cuando sucede un error se muestra en la consola de Python algunos detalles de la excepción y cómo se produjo. A esto se le llama excepción sin capturar. Cuando la excepción se generó, Python no encontró un trozo de código que estuviera previsto que la capturase y respondiera en consecuencia, por eso la excepción se fue elevando hasta llegar al nivel más alto en la consola, la cual muestra alguna información útil para la depuración del código y finaliza. Si esto sucede en la consola no es excesivamente preocupante, pero si le sucede a tu programa en plena ejecución, el programa finalizaría de forma incontrolada y no se capturase la excepción. Puede que sea lo que quieras, pero puede que no.

Python utiliza bloques try...except para manejar excepciones, y la sentencia raise para generarlas. Java y C++ utilizan bloques try...catch para manejarlas y la sentencia throw para generarlas.

El hecho de que suceda una excepción no implica necesariamente que el programa tenga que fallar. Las excepciones se pueden manejar. Algunas veces una excepción sucede porque tienes un error en el código (como por ejemplo, acceder al valor de una variable que no existe), pero en otras ocasiones puedes anticiparlo. Si vas a abrir un fichero, puede que no exista. Si vas a importar un módulo, puede que no esté instalado. Si te vas a conectar a una base de datos, puede que no esté disponible, o puede que no tengas las credenciales necesarias para acceder a ella. Si sabes que una línea de código puede elevar una excepción, deberías manejar la excepción utilizando el bloque try...except.

La función tamanyo_aproximado() eleva excepciones por dos causas: el tamaño que se pasa es mayor que el previsto, o si es menor que cero.

    if tamanyo < 0:
        raise ValueError('El número debe ser no negativo')

La sintaxis para elevar una excepción es muy simple. Utiliza la sentencia raise, seguida del nombre de la excepción y, opcionalmente, se le pasa como parámetro una cadena de texto que sirve para propósitos de depuración. La sintaxis es parecida a la de llamar a una función 1.8.

No necesitas manejar las excepciones en la función que las eleva. Si no se manejan en la función que las eleva, las excepciones pasan a la función que la llamó, luego a la que llamó a esa, y así sucesivamente a través de toda la ``pila de llamadas''. Si una excepción no se manejase en ninguna función, el programa fallará y finalizará, Python imprimirá una traza del error, y punto. Puede que fuese lo que querías o no, depende de lo que pretendieras, ¡que para eso eres el programador!

1.7.1 Capturar errores al importar

Una de las excepciones internas de Python es ImportError, que se eleva cuando intentas importar un módulo y falla. Esto puede suceder por diversas causas, pero la más simple es que el módulo no exista en tu camino de búsqueda. Puedes utilizar esta excepción para incluir características opcionales a tu programa. Por ejemplo, la librería chardet que aparece en el capítulo 15 autodetecta la codificación de caracteres. Posiblemente tu programa quiera utilizar esta librería si está instalada, pero continuar funcionando si no lo está. Para ello puedes utilizar un bloque try...except.

    try:
        import chardet
    except ImportError:
        chardet = None

Posteriormente, en el código, puedes consultar la presencia de la librería con una simple sentencia if:

    if chardet:
        # hacer algo
    else:
        # seguir de todos modos

Otro uso habitual de la excepción ImportError es cuando dos módulos implementan una API1.9 común, pero existe preferencia por uno de ellos por alguna causa (tal vez sea más rápida, o use menos memoria). Puedes probar a importar un módulo y si falla cargar el otro. Por ejemplo, en el capítulo 12 sobre XML se habla de dos módulos que implementan una API común, denominada ElementTree API. El primero lxml.etree, es un módulo desarrollado por terceros que requiere descargarlo e instalarlo tú mismo. El segundo, xml.etree.ElementTree, es más lento pero forma parte de la librería estándar de Python 3.

    try:
        from lxml import etree
    except ImportError:
        import xml.etree.ElementTree as etree

Al final de este bloque try...except, has importando algún modulo y lo has llamado etree. Puesto que ambos módulos implementan una API común, el resto del código no se tiene que preocupar de qué módulo se ha cargado1.10. Asimismo, como el módulo que se haya importado termina llamándose etree, el resto del código no tiene que estar repleto de sentencias if para llamar a diferentes módulos con diferente nombre.

1.8 Variables sin declarar

Échale otro vistazo a la siguiente línea de código de la función tamanyo_aproximado:

    multiplo = 1024 if un_kilobyte_es_1024_bytes else 1000

La variable multiplo no se ha declarado en ningún sitio, simplemente se le asigna un valor. En Python es correcto. Lo que no te dejará hacer nunca Python es referenciar a una variable a la que nunca le has asignado un valor. Si intentas hacerlo se elevará la excepción NameError.

»> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
»> x = 1
»> x
1

¡Le darás las gracias frecuentemente a Python por avisarte!

1.9 Mayúsculas y minúsculas

En Python, todos los nombres distinguen mayúsculas y minúsculas: los nombres de variables, de funciones, de módulos, de excepciones. De forma que no es el mismo nombre si cambia alguna letra de mayúscula a minúscula o viceversa.

»> un_entero = 1
»> un_entero
1
»> UN_ENTERO
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'UN_ENTERO' is not defined
»> Un_Entero
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'Un_Entero' is not defined
»> un_enteRo
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'un_enteRo' is not defined

Y así siempre si pruebas todas las combinaciones posibles.

1.10 Ejecución de scripts

Todo lo que existe en Python es un Objeto

Los módulos de Python son objetos, por lo que tienen propiedades muy útiles. Puedes utilizar alguna de ellas para probar tus módulos de una forma sencilla. Para ello puedes incluir un bloque de código especial que se ejecute cuando arrancas el fichero desde la línea de comando. Observa las últimas líneas de parahumanos.py:

if __name__ == '__main__':
    print(tamanyo_aproximado(1000000000000, False))
    print(tamanyo_aproximado(1000000000000))

Como en C, Python utiliza == para las comparaciones y = para las asignaciones. Al contrario que C, Python no permite la asignación ``en línea'', por lo que no es posible asignar un valor por accidente cuando tu intención fuese comparar.

¿Qué es lo que hace este if tan especial? Como los módulos son objetos, tienen propiedades, y una de las propiedades de los módulos es __name__. El valor de la propiedad __name__ depende de la forma en la que estés utilizando el módulo. Si importas el módulo con la sentencia import el valor que contiene __name__ es el nombre del fichero del módulo sin la extensión.

»> import parahumanos
»> parahumanos.__name__
'parahumanos'

Pero también puedes ejecutar directamente el módulo como un programa autónomo, en cuyo caso __name__ contiene el valor especial __main__. En el ejemplo, Python evaluará la sentencia if, la expresión será verdadera y ejecutará el bloque de código contenido en el if. En este caso, imprimir dos valores:

jmgaguilera@acerNetbook: /inmersionEnPython3/src$ python3 parahumanos.py
1.0 TB
931.3 GiB

¡Y así queda explicado tu primer programa Python!

1.11 Lecturas complementarias

José Miguel González Aguilera 2016-08-18