Subsecciones


4. Más herramientas de control de flujo

Además de la sentencia while, recién presentada, Python conoce las sentencias de control de flujo comunes de otros lenguajes, aunque con sabor propio.


4.1 Construcciones if

Quizá la mejor conocida de las construcciones es if (si). Por ejemplo:

>>> x = int(raw_input("Introduce un número: "))
>>> if x < 0:
...      x = 0
...      print 'Negativo cambiado a cero'
... elif x == 0:
...      print 'Cero'
... elif x == 1:
...      print 'Uno'
... else:
...      print 'Más'
...

Puede haber cero o más partes elif y la parte else (si no) es opcional. La palabra clave `elif' es una abreviatura de `else if' y evita el sagrado excesivo. Una secuencia if ... elif ... elif ... es el sustituto de las sentencias switch o case de otros lenguajes.


4.2 Sentencias for

La construcción for (para) es un poco diferente a lo acostumbrado en C o Pascal. En lugar de recorrer siempre una progresión aritmética (como en Pascal) o dejar al programador total libertad de elección de inicialización, comprobación y salto de paso (como en C), el for de Python recorre los elementos de una secuencia (por ejemplo, una lista o cadena), en el orden en que aparecen en la secuencia. Por ejemplo:

>>> # Medir algunas cadenas:
... a = ['gato', 'ventana', 'defenestrar']
>>> for x in a:
...     print x, len(x)
... 
gato 4
ventana 7
defenestrar 11

No es aconsejable modificar la secuencia que se está recorriendo (lo que sólo puede ocurrir en secuencias mutables, por ejemplo, listas). Si se necesita modificar la lista recorrida, por ejemplo, para duplicar los elementos, hay que recorrer una copia. La notación de corte hace esto muy cómodo.

>>> for x in a[:]: # hacer una copia por corte de la lista entera
...    if len(x) > 7: a.insert(0, x)
... 
>>> a
['defenestrar', 'gato', 'ventana', 'defenestrar']


4.3 La función range()

Si lo que necesitas es recorrer una secuencia de números, la función interna range() viene de perlas. Genera listas con progresiones aritméticas, por ejemplo:

>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

El punto final indicado nunca es parte de la lista generada, así que range(10) genera una lista de 10 valores, justo los índices legales de los elementos de una secuencia de longitud 10. Es posible hacer que el rango arranque en otro número o especificar un incremento diferente (incluso negativo). Este incremento se llama paso (step):

>>> range(5, 10)
[5, 6, 7, 8, 9]
>>> range(0, 10, 3)
[0, 3, 6, 9]
>>> range(-10, -100, -30)
[-10, -40, -70]

Para recorrer los índices de una secuencia, combina range() y len() de este modo:

>>> a = ['Cinco', 'lobitos', 'tiene', 'la', 'loba']
>>> for i in range(len(a)):
...     print i, a[i]
... 
0 Cinco
1 lobitos
2 tiene
3 la
4 loba


4.4 Construcciones con break, continue y else en bucles

La sentencia break (romper), como en C, salta del bucle for o while en curso más interno.

La sentencia continue (continuar), también un préstamo de C, hace que siga la siguiente iteración del bucle.

Las construcciones de bucle pueden tener una cláusula else. Ésta se ejecuta, si existe, cuando se termina el bucle por agotamiento de la lista (con for) o cuando la condición se hace falsa (con while), pero no cuando se termina el bucle con break. Para aclarar esto último, valga un ejemplo, que busca números primos:

>>> for n in range(2, 10):
...     for x in range(2, n):
...         if n % x == 0:
...            print n, '=', x, '*', n/x
...            break
...     else:
...          print n, 'es primo'
... 
2 es primo
3 es primo
4 = 2 * 2
5 es primo
6 = 2 * 3
7 es primo
8 = 2 * 4
9 = 3 * 3


4.5 Construcciones con pass

La sentencia pass no hace nada. Se puede utilizar cuando hace falta una sentencia sintácticamente pero no hace falta hacer nada. Por ejemplo:

>>> while 1:
...       pass # Espera activamente una interrupción de teclado
...


4.6 Definición de funciones

Se puede crear una función que escriba la serie de Fibonacci hasta un límite superior arbitrario:

>>> def fib(n):    # escribir la serie Fibonacci hasta n
...     "escribir la serie Fibonacci hasta n"
...     a, b = 0, 1
...     while b < n:
...         print b,
...         a, b = b, a+b
... 
>>> # Y ahora llamamos a la función recién definida:
... fib(2000)
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597

La palabra clave def introduce una definición de función. Debe ir seguida del nombre de la función y la lista entre paréntesis de los parámetros formales. Las sentencias que forman el cuerpo de la función empiezan en la siguiente línea y deben ir sangradas. La primera sentencia del cuerpo de la función puede ser una constante de cadena: esta cadena es la documentación de la función o docstring.

Existen herramientas para producir automáticamente documentación impresa/electrónica o permitir al usuario navegar por el código interactivamente. Es una buena práctica incluir documentación en el código que escribas, así que intenta hacer de ello un hábito.

La ejecución de una función introduce una tabla de símbolos nueva para las variables locales de Python. En concreto, todas las asignaciones de variables de una función almacenan el valor en la tabla de símbolos local; por lo que las referencias a variables primero miran en la tabla de símbolos local, luego en la tabla de símbolos global y, por último, en la tabla de nombres internos. Por ello no se puede asignar un valor a una variable global dentro de una función (salvo que esté mencionada en una sentencia global), pero se puede hacer referencia a ellas.

Los parámetros reales (argumentos) de una llamada a una función se introducen en la tabla de símbolos local de la función aludida al llamarla: los argumentos se pasan por valor (en donde el valor siempre es una referencia a un objeto, no el valor del objeto4.1. Cuando una función llama a otra función, se crea una tabla de símbolos locales nueva para esa llamada.

Una definición de función introduce el nombre de la función en la tabla de símbolos vigente. El valor del nombre de la función tiene un tipo reconocido por el intérprete como función definida por el usuario. Se puede asignar este valor a otro nombre y usar éste, a su vez, como función que es. Esto sirve de mecanismo de renombrado genérico:

>>> fib
<function object at 10042ed0>
>>> f = fib
>>> f(100)
1 1 2 3 5 8 13 21 34 55 89

Se puede objetar que fib no es una función, sino un procedimiento. En Python, como en C, los procedimientos son simplemente funciones que no devuelven ningún valor. De hecho, hablando técnicamente, los procedimientos sí devuelven un valor, sólo que bastante aburrido. Este valor se llama None (es un nombre interno). El intérprete suele omitir la escritura del valor de None, si es el único valor que se fuera a escribir. Se puede ver si realmente lo deseas:

>>> print fib(0)
None

Resulta simple escribir una función que devuelva una lista de los números de la serie de Fibonacci, en lugar de mostrarla:

>>> def fib2(n): # Devolver la serie de Fibonacci hasta n
...     "Devolver una lista con los números de la serie de Fibonacci hasta n"
...     resultado = []
...     a, b = 0, 1
...     while b < n:
...         resultado.append(b)    # ver más abajo
...         a, b = b, a+b
...     return resultado
... 
>>> f100 = fib2(100)    # llamarlo
>>> f100                # escribir el resultado
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
Este ejemplo, como es habitual, demuestra algunas características nuevas de Python:


4.7 Más sobre la definición de funciones

También es posible definir funciones con un número variable de argumentos. Existen tres formas, que se pueden combinar.


4.7.1 Valores por omisión en los argumentos

Es la forma más útil de especificar un valor por omisión para uno o más de los argumentos. Esto crea una función a la que se puede llamar con menos argumentos de los que tiene definidos, por ejemplo:

def confirmar(indicador, intentos=4, queja='¡O sí o no!'):
    while 1:
        respuesta = raw_input(indicador)
        if respuesta in ('s', 'si', 'sí'): return 1
        if respuesta in ('n', 'no', 'nanay', 'nasti'): return 0
        intentos = intentos - 1
        if intentos < 0: raise IOError, 'Usuario rechazado'
        print queja

Se puede llamar a esta función así: confirmar('¿Quiere salir?') o así: confirmar('¿Desea borrar el fichero?', 2).

Los valores por omisión se evalúan en el instante de definición de la función en el ámbito de definición, así:

i = 5
def f(arg = i): print arg
i = 6
f()

mostrará 5.

Aviso importante: El argumento por omisión se evalúa una sola vez. Esto lleva a diferentes resultados cuando el valor por omisión es un objeto mutable, tal como una lista o diccionario. Por ejemplo, la siguiente función acumula los argumentos que se le pasan en sucesivas llamadas:

def f(a, l = []):
    l.append(a)
    return l
print f(1)
print f(2)
print f(3)

Esto presenta:

[1]
[1, 2]
[1, 2, 3]

Si no se desea que el valor por omisión sea compartido por las sucesivas llamadas, se puede escribir la función de este modo:

def f(a, l = None):
    if l is None:
        l = []
    l.append(a)
    return l


4.7.2 Argumentos por clave

También se puede llamar a una función utilizando la forma "clave = valor". Por ejemplo, la siguiente función:

def loro(tension, estado='tieso', accion='voom', tipo='Azul noruego'):
    print "-- Este loro no podría", accion,
    print "aunque le aplicara", tension, "voltios."
    print "-- Bello plumaje, el", tipo
    print "-- ¡Está", estado, "!"

puede invocarse de estas maneras:

loro(1000)
loro(accion = 'VOOOOOM', tension = 1000000)
loro('mil', estado = 'criando malvas')
loro('un millón de', 'desprovisto de vida', 'saltar')

pero las siguientes llamadas serían todas incorrectas:

loro()                       # falta un argumento obligatorio
loro(tension=5.0, 'muerto')  # argumento clave seguido por argumento no-clave
loro(110, tension=220)       # valor de argumento duplicado 
loro(actor='John Cleese')    # clave desconocida

En general, una lista de argumentos debe tener argumentos posicionales seguidos por argumentos clave, donde las claves se seleccionan de los nombres de los parámetros formales. No importa si un parámetro formal tiene valor por omisión o no. Ningún argumento debe recibir valor más de una vez (los nombres de parámetros formales correspondientes a argumentos posicionales no se pueden usar como claves en la misma llamada). He aquí un ejemplo que falla por culpa de esta restricción:

>>> def function(a):
...     pass
... 
>>> function(0, a=0)
Traceback (innermost last):
  File "<stdin>", line 1, in ?
TypeError: keyword parameter redefined

Cuando el último parámetro formal tiene la forma **nombre recibe un diccionario que contiene todos los argumentos clave cuya clave no se corresponde con ningún parámetro formal. Esto se puede combinar con un parámetro formal de la forma *nombre (descrito en la siguiente subsección) que recibe una tupla que contiene los argumentos posicionales que exceden la lista de parámetros formales (*nombre debe aparecer antes de **nombre). Por ejemplo, si definimos una función como ésta:

def queseria(clase, *argumentos, **claves):
    print "-- ¿Tiene", clase, '?'
    print "-- Lo siento, no nos queda", clase
    for arg in argumentos: print arg
    print '-'*40
    for kw in claves.keys(): print kw, ':', claves[kw]

se puede invocar de estas maneras:

queseria('Limburger', "Chorrea mucho, señor.",
           "Chorrea mucho, muchísimo.",
           cliente='John Cleese',
           tendero='Michael Palin',
           sketch='Sketch de la quesería')

Y mostraría:

-- ¿Tiene Limburger ?
-- Lo siento, no nos queda Limburger
Chorrea mucho, señor.
Chorrea mucho, muchísimo.
----------------------------------------
cliente : John Cleese
tendero : Michael Palin
sketch : Sketch de la quesería


4.7.3 Listas de argumentos arbitrarias

Finalmente, la opción menos frecuente es especificar que una función puede llamarse con un número arbitrario de argumentos. Estos argumentos se agruparán en una tupla. Antes del número variable de argumentos puede haber cero o más argumentos normales.

def fprintf(file, formato, *args):
    file.write(formato % args)


4.7.4 Formas lambda

A petición popular, se han añadido a Python algunas características comúnmente halladas en los lenguajes de programación funcional y Lisp. Con la palabra clave lambda es posible crear pequeñas funciones anónimas. Ésta es una función que devuelve la suma de sus dos argumentos: "lambda a, b: a+b". Las formas lambda se pueden utilizar siempre que se necesite un objeto función. Están sintácticamente restringidas a una expresión simple. Semánticamente son un caramelo sintáctico para una definición de función normal. Al igual que las definiciones de funciones anidadas, las formas lambda no pueden hacer referencia a las variables del ámbito que las contiene, aunque esto se puede subsanar mediante el uso juicioso de los valores de argumento por omisión, por ejemplo:

def montar_incrementador(n):
    return lambda x, incr=n: x+incr


4.7.5 Cadenas de documentación

Hay convenciones crecientes sobre el contenido y formato de las cadenas de documentación.

La primera línea debe ser siempre un corto y conciso resumen de lo que debe hacer el objeto. En aras de la brevedad, no debes hacer constar el nombre y tipo del objeto, pues éstos están disponibles mediante otros modos (excepto si el nombre es un verbo que describe el funcionamiento de la función). Esta línea debe empezar por mayúscula y terminar en punto.

Si hay más líneas en la cadena de documentación, la segunda línea debe ir en blanco, separando visualmente el resumen del resto de la descripción. Las siguientes líneas deben ser párrafos que describan las convenciones de llamada de los objetos, sus efectos secundarios, etc.

El analizador de Python no elimina el sangrado de los literales multilínea, así que las herramientas que procesen documentación tienen que eliminar el sangrado si se desea. Esto se realiza del siguiente modo. La primera línea que no está en blanco tras la primera línea de la documentación determina el grado de sangrado de la cadena de documentación entera (no se puede utilizar la primera línea, porque suele estar pegada a las comillas de apertura y su sangrado no es evidente dentro del literal). Se elimina el espacio en blanco ``equivalente'' a este sangrado del principio de todas las líneas de la cadena. No debería haber líneas menos sangradas, pero si las hay se debe eliminar su espacio en blanco inicial. La equivalencia del espacio en blanco se debe realizar tras la expansión de los tabuladores (a 8 espacios, normalmente).

He aquí un ejemplo de una cadena de documentación multilínea:

>>> def mi_funcion():
...     """No hacer nada, pero comentarlo muy bien.
... 
...     Que no, que no hace nada.
...     """
...     pass
... 
>>> print mi_funcion.__doc__
No hacer nada, pero comentarlo muy bien.

    Que no, que no hace nada.



Notas al pie

... objeto4.1
En realidad, por referencia al objeto resultaría más correcto, ya que si se pasa un objeto mutable, el que llama verá los cambios realizados por el llamado (por ejemplo, si inserta elementos en una lista).

Ver Sobre este documento... para obtener información sobre sugerencias.