Funciones

Funciónes

En python, una función es una secuencia de llamada de los estados que van de la mano. Su propósito principal es ayudar a organizar programas en trozos que coinciden con nuestra forma de pensar sobre el problema.

La sintaxis de una definición de la función es:

def nombre (parámetros):
    CONTENIDO.

Usted puede inventar los nombres que desee para sus funciones siempre y cuando no use una palabra reservada de Python. La lista de parámetros especifica qué información, si es que la hay, se debe proporcionar a fin de usar la nueva función.

Puede haber cualquier número de estados dentro de la función, pero tienen que tener una sangría de la def. En los ejemplos en este libro, vamos a utilizar la sangría estándar de cautro espacios. Las definiciones de funciones son la segunda de las sentencias compuestas donde varias vamos a ver, todos los caules tienen el mismo patrón:

  1. Un encabezado, que empieza con una palabra reservada y termina con dos puntos (:).
  2. Un cuerpo consistente de una o más sentencias de Python, cada una de ellas con la misma sangría – 4 espacios es el estándar de Python – a partir del margen izquierdo.

Ya hemos visto el circuito for sigue este patrón.

En una definición de función la palabra reservada en el encabezado es def, y enseguida se escribe el nombre de la función y una lista de parámetros encerrados entre paréntesis. La lista de parámetros puede estar vacía o contener un sinnúmero de parámetros. En cualquier caso los paréntesis se requieren.

Supongamos que estamos trabajando con las tortugas, y una operación común que necesitamos es para dibujar cuadrados. “Dibuja un cuadrado” es una abstracción, o una parte mental, de una serie de pasos más pequeños. Así que vamos a escribir una función para capturar el patrón de este “bloque de construcción”:

import turtle


def draw_square(t, size):
    """Make turtle t draw a square with sides of length, size."""

    for i in range(4):
        t.forward(size)
        t.left(90)

turtle.setup(800, 600)
wn = turtle.Screen()
wn.bgcolor("lightgreen")
wn.title("Alex meets a function")

alex = turtle.Turtle()

# call with draw_square function with turtle, alex, and size, 250
draw_square(alex, 250)

wn.exitonclick()

Como de costumbre, ejecute :download: este programa <recursos/cap04/dibuja_cuadrado.py> y vea lo que hace.

Esta función de este programa se llama dibuja_cuadrado. Tiene dos parámetros – uno de ellos es para decirle a la función donde la tortuga necesita moverse, y el otro para saber el tamaño de el cuadrado que queremos dibujado. Asegúrese de que sabe dónde está el cuerpo de la función terminada – que depende de la sangría, y las líneas en blanco no cuentan oara este fin!

docstring

  • Si la primera cosa en un cuerpo de la función es una cadena, se llama cadena de documentación y recibe un tratamiento espacial en Python.
  • Si la primera línea en un script en Python (módulo) es una cadena, es también una cadena de documentación. Que lo vimos en el capítulo anterior, en los ejercicios que utilizan doctest.
  • Otra dorma de obtener esta información es usar el intérprete interactivo, e introduzca la expresión <function_name>,__doc__. Y se verá la cadena de documentación para la función. Lo que la cadena se escribe como la documentación en el inicio de una función es recuperable por herramientas de Python en tiempo de ejecución. Esto es diferente de comentarios en el código, que se eleiminan por completo cuando el programa se analiza.
  • Por convención, los programadores de Python utilizan las cadenas de documentación de la documentación clave de sus funciones.

Definir una nueva función no hace que la función se ejecute. Para hacerlo se necesita una llamada a función. Ya hemos visto cómo llamar a lagunas de las funciones incorporadas como la print (impresión), range (rango) y int. Las llamadas a función contienen el nombre de la función a ejecutar seguida por la lista de valores, llamados argumentos, que son asignados a los parámetros en la definición de función. Así, en las penúltima línea del programa, llamamos a la función, y pasar a Alex como la tortuga para ser manipulados, y 50 como el tamaño de el cuadrado que queremos.

Una vez que hemos definido una función, se puede llamar tantas veces como queremos, y us declaraciones se ejecuta cada vez que lo llamamos. Y que podríamos utilizar para obtener cualquiera de nuestras tortugas para dibujar un cuadrado. En el siguiente ejemplo, hemos cambiado la función dibuja_cuadrado un poco, y tess dibuja 15 cuadrados con algunas variaciones.

import turtle


def draw_multicolor_square(t, sz):
    """Make turtle t draw a multi-color square of sz."""
    for c in ['red', 'purple', 'hotpink', 'blue']:
        t.color(c)
        t.forward(sz)
        t.left(90)

# Set up the window and its attributes
turtle.setup(800, 600)
wn = turtle.Screen()
wn.bgcolor("lightgreen")
wn.title("Spiralling Squares")

# create tess and set some attributes
tess = turtle.Turtle()
tess.pensize(3)

size = 20                        # size of the smallest square
for i in range(25):
    draw_multicolor_square(tess, size)
    size = size + 10             # increase the size for next time
    tess.forward(10)             # move tess along a little
    tess.right(18)               # and give her some extra turn

wn.exitonclick()

Cuando se ejecuta este programa, que va a generar

ilustraciones/cap04/espiral_cuadrados.png

Las funciones que pueden llamar a otras funciones

¿Qué pasa si queremos una función para dibujar un rectángulo? Tendremos que separar los argumentos de anchura y altura, y un cambio en la lógica del lazo al manejar el hecho de que los cuatro lados no son iguales.

def draw_rectangle (t, w, h):
    """Hacer tortuga t para dibujar un rectángulo de ancho w y altura h."""
    for i in range(2):
        t.forward(w)
        t.left(90)
        t.forward(h)
        t.left(90)

Los nombres de los parámetros se eligen deliberadamente como una sola letra para asegurarse de que no está mal entendida. En programas reales, una vez que haya tenido más experiencia, vamos a insistir en una mejor nombres de las variables que esto.

Pero el punto es que el programa no “entiende” que va a dibujar un rectángulo, o que los parámetros representan la anchura y la altura. Conceptos como el rectángulo, el ancho y la altura son el sentido que los seres humanos tienen, no los conceptos que el programa o el ordenador entiende.

Pensar como un científico consiste en buscar patrones y relaciones. En el código anterior, lo hemos hecho hasta cierto punto. No nos limitamos a sacar cuatro lados. En su lugar, vimos que podíamos dibujar el rectángulo en dos mitades, y se utiliza un bucle para repetir ese patrón en dos ocasiones.

Pero ahora podríamos punto que un cuadrado es un tipo especial de rectángulo. Ya tenemos una función que dibuja un rectángulo, así que podemos usarlo para llamar nuestra plaza.

def draw_square (tx, sz): # una nueva versión de draw_square
    draw_rectangle (tx, Qué, Qué)

Hay algunos puntos que vale la pena señalar aquí:

  • Las funciones se pueden llamar a otras funciones.
  • Reescritura dibujar_cuadrado así a la relación entre rectángulos y cuadrados.
  • Un llamador de esta función podría decir dibujar_cuadrado (Tess, 50). Los parámetros de esta función, tx y zr, se asignan los valores del objeto de Tess, y la int 50, respectivamente.
  • En el cuerpo de la función son como cualquier otra variable.
  • Cuando la llamada se realiza a draw_rectangle, los valores de las variables tx y sz se buscan primero, y luego pasa la llamada. Así que al entrar en la parte superior de dibuje_rectangulo función, su t variable se le asigna el objeto de Tess, y w y h en función de que son a la vez le da el valor 50.

Hasta el momento, puede que no sea claro por qué vale la pena la molestia de crear todas estas nuevas funciones. En realidad, hay un montón de razones, pero este ejemplo demuestra dos:

  1. La creación de una nueva función te da la oportunidad de nombrar un grupo de declaraciones. Las funciones pueden simplificar un programa ocultando un complejo cálculo detrás de un solo comando. La función (incluyendo su nombre) puede capturar a su fraccionamiento mental, o la abstracción, del problema.
  2. Crear una nueva función puede hacer un programa más pequeño, eliminando el código repetitivo.

Como era de esperar, hay que crear una función antes de poder ejecutarlo. En otras palabras, la definición de función tiene que ser ejecutada antes de la primera vez que se llama.

Flujo de ejecución

Con el fin de asegurar que una función se defina antes de su primer uso usted tiene que saber el orden en el que las sentencias se ejecutan, este orden de ejecución se denomina flujo de ejecución. Ya hemos hablado de esto un poco en el capítulo anterior.

La ejecución siempre empieza con la primera sentencia del programa. Las sentencias se ejecutan una a una, desde arriba hacia abajo.

Las definiciones de función no alteran el flujo de ejecución del programa, pero recuerde que las sentencias que están dentro de las funciones no se ejecutan hasta que éstas sean llamadas. Aunque no es muy común, usted puede definir una función dentro de otra. En este caso, la definición interna no se ejecuta hasta que la función externa sea llamada.

Las llamadas a función son como un desvío en el flujo de ejecución. En lugar de continuar con la siguiente sentencia, el flujo salta a la primera línea de la función llamada, ejecuta todas las sentencias de la función, y regresa para continuar donde estaba previamente.

Esto suena sencillo, hasta que tenemos en cuenta que una función puede llamar a otra. Mientras se está ejecutando una función, el programa puede ejecutar las sentencias de otra función. Pero, mientras se está ejecutando la nueva función, el programa puede tener que ejecutar ¡otra función!

Afortunadamente, Python es experto en hacer el seguimiento de sus movimientos, así que cada vez que una función termina, el programa regresa al punto de la función desde donde fue llamada la función que recien fue terminada. Cuando llega al fin del programa, la ejecución termina.

¿Cual es la moraleja de esta sórdida historia? Cuando lea un programa, no lo haga de arriba hacia abajo. En lugar de esto, siga el flujo de ejecución.

Funciones que devuelven valores

La mayoría de las funciones requieren argumentos, los valores que controlan la manera en que la funciones trabajan. Por ejemplo, si usted quiere encontrar el valor absoluto de un número, tiene que indicar cuál es el número. Python tienen una función integrada en su biblioteca de funciones para calcular el valor absoluto:

>>> abs(5)
5
>>> abs(-5)
5

En este ejemplo los argumentos de la función abs son 5 y -5.

Algunas funciones necesitan más de un argumento. Por ejemplo la función de biblioteca pow toma dos argumentos, la base y el exponente. Dentro de la función, los valores que se pasan se asignan a variables llamadas parámetros.

>>> pow(2, 3)
8
>>> pow(7, 4)
2401

Otra función de biblioteca que toma más de un argumento es max.

>>> max(7, 11)
11
>>> max(4, 1, 17, 2, 12)
17
>>> max(3 * 11, 5**3, 512 - 9, 1024**0)
503

La función max puede enviar a la definición de función cualquier número de argumentos, separados por comas, y la llamada a función devolverá el máximo de los valores enviados. Los argumentos pueden ser valores simples o expresiones. En el último ejemplo la llamada a función regresa 503, puesto que es el mayor de 33, 125 y 1.

Además, funciona como área de range, int, abs todos los valores de retorno que se puede utilizar para construir expresiones más complejas.

Por lo tanto una diferencia importante entre estas funciones y como un draw_square es que draw_square no se ejecutó porque queríamos que para calcular un valor — por el contrario, escribimos draw_square porque queríamos que la ejecución de una secuencia de pasos que causó la tortuga para dibujar.

Funciones que devuelven valores se llaman funciones productivas en este curso. En algunos lenguajes de programación, una función que no devuelve un valor que se llama un procedimiento, ya que se utiliza para implementar la abstracción de procedimientos, pero en Python ambas construcciones son llamadas funciones.

¿Cómo podemos escribir nuestras propias funciones productivas? Vamos a empezar mirando algunas funciones de álgebra y ver la forma en que están escritos en Python.

Esta es una función lineal tal y como aparecería en Álgebra:

ilustraciones/cap04/f_de_x.png

Esta es la misma función en Python:

def f(x):
    return 3 * x + 5

Otro ejemplo de una función cuadrática en Álgrebra:

ilustraciones/cap04/g_de_x.png

la misma función en Python:

def g(x):
    return 4 * x ** 2 - 7

La sentencia return se sigue una expresión que se evalúa. Su resultado se devuelve a la persona que llama como el “fruto” de llamar a esta función.

Para probar estas funciones y ver cómo los valores que devuelven puede ser utilizado por el programa de llamada, primero vamos a ponerlos en nuestro propio módulo de Python. Nos presentaron a los módulos en el último capítulo, cuando se utilizó el módulo de tortuga. Ahora vamos a hacer la nuestra.

Cree un archivo llamado algebra_funciones.py que contiene las definiciones de función de f y g.

Iniciar un shell de Python desde el mismo directorio en el que se encuentra algebra_funciones.py, y tratar de estos comandos en el indicador de Python

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
>>> from algebra_funciones import f, g
>>> f(0)
5
>>> g(0)
-7
>>> g(2)
9
>>> f(9)
32
>>> returned_value = g(2)
>>> returned_value
9
>>> f(returned_value)
32
>>> f(g(2))
32
>>>

Algunas cosas que hay que aprender de este ejemplo es:

  1. La declaración de importación se puede utilizar para leer nuestro código de python propio en el intérprete.

    Así, podemos escribir nuestras funciones en un módulo y luego importarlos en el intérprete, para que podamos experimentar con ellas.

  2. La declaración de import (importación) tiene dos formas

      import MODULE
    
      from MODULE import FUNCION, FUNCION, ...
    
    Hemos utilizado la primera forma el último capítulo al importar el módulo de    ``turtle`` (tortuga). Si utilizamos la misma forma que en esta ocasión, la
    sesión de Python habría comenzado como este:
    
    .. sourcecode:: python
    
       >>> import algebra_funciones
       >>> algebra_funciones.f(0)
       5
       >>> algebra_funciones.g(0)
       -7

Composición

Asi como las funciones matemáticas, las funciones de Python pueden componerse, esto es, puede usar el resultado de una función como entrada de otra

>>> muestra_doble(abs(-7))
7 7
>>> muestra_doble(max(3, 1, abs(-11), 7))
11 11

En el primer ejemplo, abs(-7) produce 7, el cual es ahora el argumento de la función muestra_doble. En el segundo ejemplo tenemos dos niveles de composición puesto que primero se evalúa abs(-11) que produce 11, y luego se evalúa max(3, 1, 11, 7) que da como resultado 11; finalmente la función muestra_doble(11) muestra el resultado.

También podemos usar una variable como un argumento:

>>> miguel = 'Eric, el medio abeja.'
>>> muestra_doble(miguel)
Eric, el medio abeja. Eric, el medio abeja.

Note algo muy importante en este ejemplo: el nombre de la variable que pasamos como argumento (miguel) no tiene nada que ver con el nombre del parámetro (param). Es decir, no importa cómo se nombraba el valor originalmente (desde la llamada de función); aquí, en la función muestra_doble llamamos a cualquiera de ellos param.

Las variables y los parámetros son locales

Cuando usted crea una variable local en una función, solamente existe dentro de la función y no fuera de ella. Por ejemplo:

def concatena_doble(parte1, parte2):
    concatena = parte1 + parte2
    muestra_doble(concatena)

Esta función toma dos argumentos, los concatena, y luego muestra el resultado dos veces. Podemos llamar a la función con dos cadenas:

>>> cantico1 = "Pie Jesu domine, "
>>> cantico2 = "Dona eis requiem."
>>> concatena_doble(cantico1, cantico2)
Pie Jesu domine, Dona eis requiem. Pie Jesu domine, Dona eis requiem.

Cuando concatena_doble termina, la variable concatena se destruye. Si intentamos mostrarla obtenemos un error:

>>> print concatena
NameError: name 'concatena' is not defined

Los parámetros también son locales. Por ejemplo, afuera de la función muestra_doble, no existe algo como param. Si usted intenta usarlo Python se quejará.

Diagramas de pila

Para seguir la pista de qué variables se usan en qué lugares es útil dibujar un diagrama de pila. Como los diagramas de estado, los diagramas de pila muestran el valor de cada variable y además muestran a qué función pertenece cada una.

Cada función se representa por un marco. Un marco es una recuadro con el nombre de una función al lado y los parámetros y variables adentro. El diagrama de pila para el ejemplo anterior luce así:

diagrama de pila

El orden de la pila mustra el flujo de ejecución. La función muestra_doble fue llamada por concatena_doble, y concatena_doble fue llamada por __main__, que es un nombre especial para la función de más alto nivel. Esto significa que cuando usted crea una variable afuera de cualquier función, tal variable pertenece a __main__.

Cada parámetro se refiere al mismo valor que su argumento correspondiente. Así, parte1 tiene el mismo valor que cantico1, parte2 tiene el mismo valor que cantico2, y param tiene el mismo valor que concatena.

Si sucede un error durante una llamada de función, Python muestra el nombre de ésta, el nombre de la función que la llamó, el nombre de la función que llamó a esta otra, y así sucesivamente hasta llegar a la función de más alto nivel.

Para ver cómo funciona esto cree un guión en Python que se llame pruebame2.py que luce como sigue:

def muestra_doble(param):
    print param, param
    print concatena

def concatena_doble(parte1, parte2):
    concatena = parte1 + parte2
    muestra_doble(concatena)

cantico1 = "Pie Jesu domine, "
cantico2 = "Dona eis requim."
concatena_doble(cantico1, cantico2)

Hemos agregado la sentencia print concatena en la función muestra_doble; sin embargo, concatena no está definida en esta función. Al ejecutar este guión se producirá un mensaje de error como el siguiente:

Traceback (innermost last):
  File "pruebame2.py", line 11, in <module>
    concatena_doble(cántico1, cántico2)
  File "pruebame2.py", line 7, in concatena_doble
    muestra_doble(concatena)
  File "pruebame2.py", line 3, in muestra_doble
    print concatena
NameError: global name 'concatena' is not defined

Esta lista de funciones se denomina trazado inverso. Nos informa en qué archivo de programa ocurrió el error, en qué línea, y qué funciones se estaban ejecutando en ese momento. También muestra la línea de código que causó el error.

Note la similitud entre el trazado inverso y el diagrama de pila. Esto no es una coincidencia. De hecho, otro nombre común para el trazado inverso es trazado de pila.

Glosario

función
Secuencia de sentencias etiquetadas que llevan a cabo determinada operación de utilidad. Las funciones pueden tomar parámetros o no, y pueden producir un resultado o no.
definición de función
Sentencia que crea una nueva función, especificando su nombre, parámetros y las sentencias que ejecuta.
sentencia compuesta

Sentencia que consiste de dos partes:

  1. encabezado - que empieza con una palabra reservada que determina el tipo de sentencia, y finaliza con dos puntos.
  2. cuerpo - que contiene una o más sentencias con el mismo sangrado desde el encabezado.

La sintaxis de una sentencia compuesta se ve así:

.. sourcecode:: python
palabra_reservada expresión:
sentencia sentencia ...
encabezado
Primera parte de una sentencia compuesta. Los encabezados empiezan con una palabra reservada y terminan con dos puntos.
cuerpo
Segunda parte de una sentencia compuesta. El cuerpo consiste de una secuencia de sentencias sangradas las misma distancia a partir del inicio del encabezado. El sangrado usual usado en la comunidad Python es de 4 espacios.
llamada a función
Una sentencia que ejecuta una función. Está compuesta por el nombre de la función más una lista de argumentos encerrados entre paréntesis.
flujo de ejecución
Orden en el que se ejecutan las sentencias durante la ejecución de un programa.
parámetro
Nombre que se usa dentro de una función para referirse al valor que se le pasa como argumento.
import

Sentencia que permite que las funciones y variables definidas en un guión de Python estén disponibles en el ámbito de otro guión o en una sesión interactiva con la terminal de Python.

Por ejemplo, suponga que las siguientes instrucciones están en un guión llamado pruebame.py:

def muestra_tres_veces(algo):
    print algo, algo, algo

    n = 42
    c = "Y ahora algo completamente diferente..."

Ahora inicie una sesión en la terminal de Python desde el mismo directorio donde se localiza pruebame.py:

$ ls
pruebame.py  &lt;y otras cosas...&gt;
$ python
>>>

Se han definido tres nombres en pruebame.py: muestra_tres_veces, n, y c. Si intentamos acceder a cualquiera de ellos desde la terminal, sin haberlos importado primero, obtenemos un error:

>>> n
Traceback (most recent call last):
  File <stdin>", line 1, in <module>
NameError: name 'n' is not defined
>>> muestra_tres_veces("¡ay!")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'muestra_tres_veces' is not defined

Sin embargo, si importamos todo el contenido de pruebame.py, podemos usar todo lo que ahí está definido:

>>> from pruebame import *
>>> n
42
>>> c
'Y ahora algo completamente diferente...'
>>> muestra_tres_veces("¡Hurra!")
¡Hurra! ¡Hurra! ¡Hurra!
>>>

Nótese que usted no incluyó la extensión .py del nombre del guión en la sentencia import.

argumento
Valor que se le pasa a una función cuando se la llama. El valor se asigna al parámetro correspondiente de la función.
composición de función
Uso de la salida de una llamada a función como la entrada de otra.
variable local
Variable definida dentro de una función. Las variables locales sólo pueden usarse dentro de su función.
diagrama de pila
Representación gráfica de una pila de funciones, sus variables y los valores a los que se refieren.
marco
Recuadro que en un diagrama de pila representa una llamada a función. Contiene las variables locales y los parámetros de la función.
trazado inverso
Listado de las funciones en curso de ejecución, presentadas cuando sucede un error en tiempo de ejecución. Es común que al trazado inverso también se le conozca como trazado de pila porque lista las funciones en el orden en el cual son almacenadas en la pila de llamadas.

Ejercicios

  1. Con un editor de texto cree un guión de Python que se llame pruebame3.py. Escriba en este archivo una función que se llame nueve_lineas que use la función tres_lineas para mostrar nueve líneas en blanco. Enseguida agregue una función que se llame limpia_pantalla que muestre veinticinco líneas en blanco. La última instrucción en su programa debe ser una llamada a limpia_pantalla.

  2. Mueva la última instrucción del archivo pruebame3.py al inicio del programa, de forma tal que la llamada a la función limpia_pantalla esté antes que la definición de función. Ejecute el programa y registre qué mensaje de error obtiene. ¿Puede establecer una regla sobre las definciones de funciones y las llamadas a función que describa la posición relativa entre ellas en el programa?

  3. Utilizando la versión que funciona de pruebame3.py mueva la definición de nueva_linea después de la definción de tres_líneas. Registre qué sucede cuando ejecuta el programa. Ahora mueva la definición de nueva_linea debajo de una llamada a tres_líneas(). Explique cómo es que éste es un ejemplo de la regla que estableció en el ejercicio previo.

  4. Cree el cuerpo de la definición de función de concatena_n_veces de forma tal que muestre la cadena c, n veces:

    def concantena_n_veces(c, n):
        "Cree su código aquí"
    

    Guarde este función en un guión llamado prueba_import.py. Ahora en el indicador de Unix asegúrese de que usted se encuentra en el mismo directorio donde se localiza el archivo prueba_import.py (el comando ls debe mostrar el archivo prueba_import.py). Inicie la terminal de Python y pruebe lo siguiente:

    >>> from prueba_import import *
    >>> concatena_n_veces('Basura', 7)
    BasuraBasuraBasuraBasuraBasuraBasuraBasura
    

    Si todo sale bien su sesión lucirá como esta. Experimente con otras llamadas a concatena_n_veces hasta que usted se sienta a gusto con los resultados obtenidos.