El lenguaje Python
Introducción
Ahora que los tipos de datos no tienen secretos, es hora de entrar en más detalles sobre el lenguaje Python en sí.
La función print()
La primera función que vamos a estudiar es, por supuesto, la función print().
Desde la versión 3 de Python es una función. Antes era una instrucción, por lo que no era necesario paréntesis.
De manera predeterminada, print() muestra el objeto solicitado y luego envía un final de línea (Line Feed).
Este comportamiento se puede modificar con la opción end, que le permite especificar lo que desea al final.
>>> for x in [1,2,3]:
... print(x , end="-")
...
1-2-3->>> # sin fin de línea ... desde '-' en lugar
print() puede mostrar también varios valores de golpe.
>>> print(1,2,3)
1 2 3
print() permite modificar el separador en este caso.
>>> print(1,2,3, sep="/")
1/2/3
Pero sobre todo, print() permite formatear los datos de dos maneras:
Primero llegó el formateo C-STYLE, al igual que printf() en lenguaje C o el comando printf del shell.
Ejemplo
>>> print( "[ %10s ]" % "hello")
[ hello ]
Luego vino el formateo con el comando format(), que permite generar documentos de una manera simple y elegante.
>>> print( "[ {} al {} ]".format( "hola", "mundo" ))
[ hola al mundo ]
1. Print() formateado C-STYLE
Este método de formateo casi desapareció del lenguaje Python, pero los módulos grandes como el módulo ’logging’ todavía lo usan y nuevos tipos de datos, como la clase ’bytes’, finalmente lo usarán.
"<cadena con formato>" % valor
o
"<cadena con múltiples formatos>" % (valor, valor, ...)
Y como a muchos, nos resulta bastante fácil escribir a máquina, además de que algunos hábitos son difíciles de romper.
La expresión del formato:
-
El carácter ’%’ indica el inicio del formato
-
Una opción (# 0 <espacio> - +)
-
Una clave (opcional) entre paréntesis (nombre_de_campo)
-
Una indicación de longitud mínima (opcional)
-
Una indicación opcional formada por un punto (’.’), seguida de un número, que se utiliza para definir la precisión después...
Las estructuras condicionales
La estructura condicional, deberíamos decir, porque solo existe if ... elif ... else.
La sintaxis es, como el resto del lenguaje, simple:
if <condición>:
<bloque de código>
Si la condición es verdadera, ejecutamos el bloque de código.
elif es una contracción de else if y podemos hablar de un "bloque de prueba" como hablamos de un bloque de código.
Un bloque de prueba comienza con una instrucción if, puede ir acompañado de una o más instrucciones elif y posiblemente de una instrucción else al final.
En una sucesión de if .. elif .. elif, las condiciones se evalúan una tras otra hasta que una de ellas es verdadera y, en este caso, ejecutamos el bloque de código asociado y salimos del "bloque de prueba".
Si ninguna condición es verdadera y hay una instrucción else, ejecutamos el bloque de código asociado a esa instrucción else.
De lo contrario, salimos del bloque de código.
if <condición1>:
<bloque de código 1>
elif <condición2>:
<bloque de código 2>
elif <condición3>:
<bloque de código 3>
...
else:
<bloque de código else>...
Los bucles
Hay dos formas de hacer bucles en Python: bucles ’for’ y bucles ’while’.
Como de costumbre, comenzamos por lo más simple.
La instrucción while tiene como sintaxis:
while <condición>:
<bloque de código>
Siempre que la condición sea verdadera, ejecutamos el bucle en el bloque de código.
La instrucción for se basa en una lista de valores y una variable que contiene el valor actual.
for <variables> in <lista de valores>:
<bloque de código>
Siempre que podamos alimentar la variable con la lista de valores, ejecutamos el bloque de código.
La lista de valores detrás de la instrucción in puede ser cualquier objeto que le permita recorrer sus valores, lo que se denomina un iterable.
<variables> está en plural, no es un error tipográfico. El motivo se revelará en unas pocas páginas.
En los tipos de datos iterables en Python se incluyen cadenas de caracteres, listas, diccionarios, tuplas y sets.
1. Algunos ejemplos simples
# archivo: loop0.py
# Bucle while
contador = 1
while contador <= 10:
print("Contador: %s " % contador)
contador += 1
# Bucle for
for n in [1,2,3]:
print(n)
## Ejemplo en modo interactivo
>>> for c in "abcd":
... print(c)
...
a
b
c
d
2. La función range()
Es posible que haya observado que Python no ofrece un bucle con contadores integrados. Para eso, es necesario utilizar la función range().
range([inicio,] fin [, paso]]) genera un objeto iterable que comienza en "inicio" y termina en "fin" con un incremento de "paso".
El argumento inicio es opcional; por defecto, comienza desde cero.
El incremento paso es opcional y está por defecto a 1.
Ejemplo
>>> for x in range(0,5):
... print(x)
...
0
1
2
3
4
>>> for x in range(1,10,2):
... print(x)
...
1
3
5
7 ...
Las funciones
Evidentemente, el lenguaje Python permite crear sus propias funciones.
Esto permite factorizar el código y dividir un algoritmo complejo en subconjuntos más simples.
En cuanto al resto, una función se define simplemente con la instrucción ’def’ de la siguiente manera:
def <nombre de la función>(argumentos):
<bloque de código>
Los nombres de las funciones están sujetos a las mismas reglas que para las variables (sin palabras reservadas, sin cifra al inicio, etc.) y, por convención, comienzan con una letra minúscula.
Los nombres en Python que comienzan con una letra mayúscula, generalmente denotan una clase (String, Float, etc.) pero, nuevamente, esto es una convención.
Una vez definida la función, forma parte del arsenal estándar y se puede utilizar como las funciones nativas de Python (print(), len(), max()…).
Ejemplo
>>> def di_hola():
... return "Hola"
...
>>> di_hola()
'Hola'
Detalle importante: los paréntesis. Son necesarios para distinguir una variable de una función.
En el ejemplo anterior, la función devuelve un valor, pero no es obligatorio.
Si la función no usa la instrucción return, entonces es un procedimiento, y en este caso devuelve el valor None.
>>> def f1(): # una función
... return "Hola"
...
>>> def p1(): # una función sin 'return'
... print("HOLA") # = un procedimiento para los puristas
...
>>> a = p1()
HOLA
>>> print(a)
None
>>> a = f1()
>>> print(a)
Hola
Está bien poder crear funciones. Cualquier lenguaje digno de ese nombre debe permitirlo.
Pero aún hay que poder transmitir argumentos a estas funciones y ahí Python sorprende por su eficiencia y su sencillez.
1. Los argumentos de funciones con Python
En primer lugar, consideramos...
Los módulos y los paquetes
El intérprete de Python está bien al principio, pero ahora se deberían alcanzar los límites con esta herramienta.
Cada vez hay más archivos de scripts de Python en su directorio e inevitablemente, se hacen cada vez más largos.
Además, a fuerza de practicar este fabuloso lenguaje, ciertas funciones aparecen repetidamente en sus scripts.
Es el momento de empezar a reestructurar su código, separándolo en varios archivos y por tanto, creando módulos. Cuando haya muchos módulos, será posible estructurarlos en paquetes.
Así es como se estructura la biblioteca estándar de Python, o "stdlib"; esto se estudiará en el capítulo Librería estándar y algunas otras dedicado a ella.
Un módulo es un archivo en el que encontramos definiciones de variables y funciones, que podemos llamar con la instrucción import.
Un primer módulo:
# archivo: paquetes/modulo1.py
##
## Módulo 1
##
M1_VAR = 10 # una variable
def M1_f1():
print("Módulo 1: f1") # una función
Una vez creado el archivo, es posible importar este módulo de dos formas:
import <módulo>
o
from <módulo> import <* | objeto del módulo>
La primera sintaxis, import nombre_del_modulo, lee el archivo nombre_del_modulo.py e interpreta todo el código presente en el archivo.
Así, los objetos contenidos en el módulo se crean en el contexto de ejecución.
Para llamar a estos objetos, debe utilizar como prefijo el nombre del módulo.
Ejemplo con el archivo modulo1.py
>>> import modulo1
>>> print( modulo1.M1_VAR )
10
>>> modulo1.M1_f1() # llamada a la función
Módulo 1: f1 # resultado
De la misma manera que:
>>> import random
>>> random.random()
0.9565541682756001
La segunda sintaxis, from <módulo> import <objeto>, le permite importar...
Las excepciones y la gestión de errores
En esta parte, se aborda un punto importante del lenguaje Python: la gestión de errores y excepciones.
Es común juzgar la calidad de un programa no por el hecho de que nunca falle, sino por la cantidad de errores o excepciones que puede manejar.
En primer lugar, debe distinguir entre errores de sintaxis y excepciones.
Los errores de sintaxis se detectan durante el análisis de código: olvido de ’:’, error de escritura en una instrucción, etc.
Las excepciones solo ocurren durante la ejecución del script; la sintaxis es correcta y no es la causa, pero el script hace algo imposible, como dividir por cero, usar una variable que no existe o una mezcla de tipos de datos.
Todas estas excepciones están definidas en el lenguaje Python:
ZeroDivisionError |
para la división por cero |
NameError |
para variables inexistentes |
TypeError |
para excepciones relacionadas con el tipo |
Hay muchas más. Se pueden encontrar en la documentación oficial en línea del lenguaje Python en https://docs.python.org/es/3 (par la versión 3 de Python traducida a español).
1. Las instrucciones try … except … finally
Las instrucciones para segurizar un bloque de código son try y except, con la siguiente sintaxis:
try:
<bloque de código a segurizar>
except:
<bloque de código en caso de excepción>
Se ejecuta el bloque de código entre try y el primer except.
Si no se produce ninguna excepción, seguimos después del bloque de gestión de errores.
En caso de excepción, se interrumpe la ejecución del "bloque de código a segurizar" y se ejecuta el "bloque de código en caso de excepción", después de except.
En esta sintaxis, se tienen en cuenta todas las excepciones.
Pero es posible ser más preciso y especificar bloques de código por excepciones.
Como esto:
try:
<bloque de código a segurizar>
except ( Excepcion1, Excepcion2 ):
<bloque de código Excepcion1 y Excepcion2>
except...
Las entradas/salidas (archivo y otros)
Hasta ahora, las instrucciones de Python estudiadas interferían poco con el mundo exterior.
Ahora es el momento de ver qué ofrece el lenguaje Python para las entradas /salidas, y más específicamente para los archivos.
Nada particularmente nuevo, simplemente abra el archivo con la instrucción open() para recuperar un objeto en particular: un descriptor de archivo.
Este objeto incluye varios métodos para leer, leer líneas, moverse en el archivo y escribir.
Un simple ejemplo para empezar:
#archivo: archivo1.py
archivo = "data.txt" # el nombre del archivo
fd = open(archivo) # apertura
for l in fd: # lectura línea a línea
print("l=%s" % l, end='') # utilización de los datos
fd.close() # cierre
El archivo data.txt contiene algunas líneas de texto.
Como aquí:
Línea 1 del archivo
Línea 2 del archivo
Línea 3 del archivo
Línea 4 del archivo
Línea 5 del archivo
Además, para generarlo puede resultar útil el siguiente ejemplo:
#archivo: archivo2.py
archivo = "data.txt"
fd = open(archivo, "w") # apertura en
# modo escritura
for n in range(1, 6):
f.write("Línea No %s del archivo\n" % n) # escritura
f.close() ...
La instrucción With
Recorrer un descriptor de archivo con un bucle ’for’ ya es una forma simple y eficiente de hacer las cosas o más bien, una forma pythonesca de hacer las cosas.
Pero Python puede hacerlo mejor, como la instrucción with.
La sintaxis siempre está en la misma línea:
with <expresión> [as variable]:
<bloque de código>
expresión debe devolver un objeto compatible con el protocolo de ’Administrador de contexto’ (este se estudiará más adelante).
Pero si nos ceñimos a las grandes líneas, es posible usarlo de la siguiente manera:
# ejemplo con la apertura de un archivo (caso típico)
with open(archivo) as f:
<bloque de código>
Al final del bloque de código, se cerrará el archivo y se eliminará la variable f, que representa al descriptor del archivo.
A continuación se muestra una forma muy pythonesca de leer los campos del famoso archivo /etc/passwd:
#archivo: lectura_password2.py
with open('/etc/passwd') as f:
for l in f:
user, pswd, id_u, id_g, com, home, shell = l.split(':')
print( "USER = %s" % user )
print( "UID ...
Ejemplo de script: hdump
Ahora, estudiemos un pequeño ejemplo: la visualización hexadecimal de cualquier archivo; una versión simple del comando de sistema hd o hexdump.
Hay una noción previa sobre la variable sys.argv: esta contiene los argumentos de la línea de comandos cuando lanzamos el script.
Ejemplo
python hdump.py nombre_de_archivo
En este caso, sys.argv es una lista que contiene:
sys.argv[0] |
=> hdump.py |
el nombre del script |
sys.argv[1] |
=> nombre_de_archivo |
el primer argumento de la línea de comandos |
Ahora que se conoce el principio básico, es fácil verificar que si el número de apariciones de la lista sys.argv, es decir len(sys.argv), es igual a 2. Entonces, sí tenemos algo como argumento y podemos continuar.
Segunda noción un poco más avanzada: os.path.exists() es una función del módulo ’os.path’ que devuelve True si el archivo existe.
El script en sí está estructurado de la siguiente manera:
-
Importación e inicialización de módulos.
-
Función uso(), cuya función es mostrar el uso del comando.
-
Función hdump(), que cortará el archivo en trozos y lo mostrará.
-
Programa principal que se encarga de algunas comprobaciones:
-
si hay al menos un argumento
-
si el argumento es un nombre de archivo existente
entonces se ejecuta la función hdump, con el nombre del archivo como argumento
en caso contrario, se ejecuta la función uso().
-
La función principal hdump() no es muy compleja: se trata de un bucle while, cuya salida está controlada por la variable EOF.
Antes de iniciar el bucle, realizamos algunas inicializaciones y, sobre todo, abrimos el archivo en modo lectura binaria.
Después, se trata de la repetición de las siguientes secuencias:
-
Lectura de un número de bytes igual a la variable BUFFER y asignación a la variable record, que por tanto se convierte en una lista de ’bytes’, un tipo de objeto Python dedicado.
-
Si el número de bytes leídos es diferente de BUFFER, es porque es el final del archivo.
-
Gestión del número...
Resumen
Este capítulo es importante porque le permite descubrir las principales instrucciones del lenguaje Python. Todavía quedan algunas, incluida la instrucción class, que abre la puerta a la programación orientada a objetos (POO).
Desde el principio se ha dicho que con Python todo es un objeto, ahora es el momento de ver realmente de qué se trata.