Introducción a la programación en Python

clase 02b
Funciones (II)

Funciones definidas por el usuario

Aparte de las funciones integradas al lenguaje (built-in) y de las que se puedan importar de bibliotecas externas, se pueden integrar al lenguaje funciones definidas por el usuario.

  1. La sentencia para definir una función en Python comienza con la palabra clave def más el nombre de la función a definir seguido de paréntesis, dentro de los cuales va el o los parámetros que recibe la función (si son más de uno, separados por coma). La sentencia se termina con dos puntos (:)
  2. Luego viene el bloque de código que conforma el cuerpo de la función; debe ir indentado (por convención: 4 espacios)
  3. Si la función devuelve un valor, se hace con la instrucción return.
  4. La definición de la función termina cuando termina el bloque de código indentado.
def nombre_de_funcion(par1, par2,... parN):
    [bloque de código (indentado)]
    [return objeto]

nombre de las funciones

  • para los nombres de las funciones rigen las mismas condiciones que para los nombres de las variables (ver clase 1).
  • es conveniente dar a la función un nombre descriptivo de su funcionalidad

creando funciones

Para crear una función es esencial definir con total precisión qué queremos que haga.
Es decir:

  • qué queremos ingresar a la función como argumento(s)
  • qué queremos que devuelva o haga la función

Ejemplo 2.1

Vamos a crear una función que convierta un valor de temperatura en grados Fahrenheit a grados Celsius (ver Ejemplo 1.1)
La función va a tener un único argumento, que es el valor de temperatura en Fahrenheit, y va a devolver el valor en grados Celsius.

In [1]:
def fahrenheit_to_celsius(grados_fahrenheit):
    grados_celsius = (grados_fahrenheit-32)*5/9
    return grados_celsius

temp1 = fahrenheit_to_celsius(80)
print(temp1)
print(fahrenheit_to_celsius(66))
temp2 = 75
print(fahrenheit_to_celsius(temp2))
26.666666666666668
18.88888888888889
23.88888888888889

Ejemplo 2.2

Vamos a crear una función que, partir de una frecuencia dada, calcule la frecuencia a un número determinado de cents de distancia (ver Ejemplo 1.2).

In [2]:
def cents_a_frecuencia(frec_inicial, cents):
    frec_final = frec_inicial*(2**(cents/1200))
    return frec_final

frec1 = 440
frec2 = cents_a_frecuencia(frec1, 50)
print(frec2)
frec3 = cents_a_frecuencia(frec1, -1200)
print(frec3)
452.8929841231365
220.0

Ejemplo 2.3

Vamos a definir una función que, dado un número de divisiones iguales de un intervalo "octavante" (no necesariamente 2), calcule el multiplicador de frecuencia entre grados sucesivos (por ejemplo, para la división en 12 partes iguales de la octava 2:1, el valor 1.0594630943592953). (ver Ejercicio 1.1)

Solución: Para una cantidad N de divisiones de un intervalo O, el valor del multiplicador de frecuencia entre grados conjuntos es raíz N-ésima de O.

In [15]:
def division(grados, octava):
    factor = octava**(1/grados)
    return factor

et12 = division(12, 2)
print(et12)
et19 = division(19, 2)
print(et19)
foo = division(23, 3)
print(foo)
1.0594630943592953
1.0371550444461919
1.0489249176452926

Orden de los parámetros

Por defecto, los argumentos que se pasan a la función son interpretados en el orden en que están los parámetros en la definición (argumentos posicionales). Pero se pueden pasar los argumentos en cualquier orden arbitrario, usando los nombres de los parámetros (argumentos por clave).

In [5]:
et12 = division(octava=2, grados=12)
print(et12)
1.0594630943592953

Parámetros por defecto

En una función podemos determinar que un parámetro sea opcional y adquiera un valor por defecto, asignándole un valor en la definición de la función.

Por ejemplo, en la función anterior podemos decidir que por defecto el valor de octava es 2, y sólo necesitamos explicitar ese parámetro cuando queremos un valor diferente.

In [6]:
def division(grados, octava=2):
    factor = octava**(1/grados)
    return factor

et12 = division(12)
print(et12)
et19 = division(19)
print(et19)
foo = division(23,3)
print(foo)
1.0594630943592953
1.0371550444461919
1.0489249176452926

Alcance de las variables

Por defecto, las variables en Python son locales. Esto quiere decir que las variables definidas y utilizadas en el bloque de código de una función, sólo tienen existencia dentro de la misma, y no interfieren con otras variables del resto del código.

A su vez, las variables existentes fuera de una función, no son visibles dentro de la misma.

In [7]:
var1 = "variable original"

def varmod():
    var1 = "variable modificada"
    print(var1)

print(var1)
varmod()
print(var1)
variable original
variable modificada
variable original

En caso de que sea conveniente o necesario, una variable puede convertirse en global declarándola explícitamente como tal con la instrucción global.

In [8]:
var1 = "variable original"

def globvar():
    global var1
    var1 = "variable global modificada"

print(var1)
globvar()
print(var1)
variable original
variable global modificada

Como se puede ver, después de llamar a la función globvar(), la variable var1 queda modificada.
En general, este procedimiento debe utilizarse con precaución.

Docstrings

Las docstrings (o documentation strings) son, como lo dice su nombre, cadenas de texto que sirven para documentar diversos aspectos de una función: qué hace, el valor de sus parámetros, qué tipos de argumentos recibe, qué tipo de objeto devuelve, etc.

Aparecen como primera instrucción en el cuerpo de la función, a continuación de su declaración, y por convención van delimitadas por triple comillas.

In [9]:
def fahrenheit_to_celsius(grados_fahrenheit):
    """convierte una temperatura en grados Fahrenheit
    a grados Celsius"""
    grados_celsius = (grados_fahrenheit-32)*5/9
    return grados_celsius

En casos simples, la doctring consiste en una sola línea, con una descripción sumaria de la función.

Funciones más complejas pueden requerir una descripción más detallada. En ese caso, por convención se deja una línea en blanco después de la primera línea con la descripción sumaria, y luego se procede a expandir la descripción.

Una funcionalidad valiosa de las docstrings, es que Python genera a partir de ellas su propia documentación, que se puede invocar en línea mediante la función help() vista más arriba.

In [16]:
help(fahrenheit_to_celsius)
Help on function fahrenheit_to_celsius in module __main__:

fahrenheit_to_celsius(grados_fahrenheit)
    convierte una temperatura en grados Fahrenheit
    a grados Celsius

Al invocar la función help() pasándole como argumento el nombre de una función, nos devuelve la siguiente información:

  1. el módulo al cual pertenece la función, en este caso, el cuerpo del programa principal, indicado por el nombre de módulo \_\_main\_\_. Puede también ser una función integrada (módulo built-in, como en el ejemplo de help() expuesto arriba), o una función de un módulo importado, como se verá más adelante.
  2. la sintaxis básica de la función, tal cual fue declarada con la instrucción def
  3. toda la documentación que se haya incluido en la docstring.

Parámetros y salida

Se pueden definir funciones que no tengan parámetros de entrada. En ese caso, la función ejecuta el mismo código cada vez que es invocada.

In [12]:
def alarma():
    print("riiiiiiiiing")

alarma()
riiiiiiiiing

La función anterior tampoco utiliza la intrucción return para devolver un valor.
En realidad, toda función en Python devuelve un valor. Cuando no se utiliza la instrucción return para devolver un valor explícito, la función devuelve el valor None.

In [13]:
valor = alarma()
print("El valor que devuelve la función alarma() es", valor)
riiiiiiiiing
El valor que devuelve la función alarma() es None

Comentarios

Los comentarios en el código tienen una vital importancia en el desarrollo de todo programa.

Son ignorados por el intérprete y no generan ningún tipo de código, pero constituyen una ayuda esencial tanto para quien está desarrollando el programa, como para otras personas que lean el código.

Entre otras, algunas de las funciones más importantes que pueden cumplir los comentarios en un programa, son:

  • brindar información general sobre el programa
  • explicar qué hace cada una de sus partes
  • aclarar y/o fundamentar el funcionamiento de un bloque específico de código, que no sea evidente de su propia lectura
  • indicar cosas pendientes para agregar o mejorar

El signo para indicar el comienzo de un comentario en Python es la almohadilla o numeral (#), a partir del cual y hasta el fin de la línea, todo se considera un comentario y es ignorado por el intérprete.

El carácter # puede estar al comienzo de línea (en cuyo caso toda la línea será ignorada), o después de finalizar una instrucción válida de código.

Como ejemplo, se muestra una forma posible de comentar el programa del Ejemplo 1.2 (clase 1).

In [14]:
# programa que calcula la frecuencia resultante al transportar
# una frecuencia inicial dada una cierta cantidad de cents

# primero se pide al usuario que ingrese la frecuencia incial 
# y la cantidad de cents a ser transportada

# se piden los argumentos de entrada
# y se convierten los strings en un números flotantes
frec_inicial = float(input("ingrese la frecuencia inicial: "))
cents = float(input("ingrese la cantidad de cents: "))
# se calcula la frecuencia resultante
frec_final = frec_inicial*(2**(cents/1200))

print("frecuencia final: ", frec_final)
ingrese la frecuencia inicial: 440
ingrese la cantidad de cents: -1200
frecuencia final:  220.0

En un programa tan corto y sencillo quizás no sean necesarios tantos comentarios. Es importante escribir programas suficientemente comentados, pero no recargar el código con comentarios innecesarios de cosas obvias.

Por ejemplo, el comentario siguiente sería inadecuado, por resultar obvio de la propia instrucción:

In [ ]:
# imprime la frecuencia resultante en la pantalla
print("frecuencia final: ", frec_final)

comentarios multilínea

A diferencia de otros lenguajes de programación, Python no dispone de un método para delimitar bloques de comentarios de varias líneas.

Las alternativas para introducir comentarios multilíneas son:

  1. comentar cada una de las líneas con el carácter #: en general todos los editores de programación y entornos de desarrollo (IDEs) disponen de mecanismos que permiten comentar y descomentar fácilmente un conjunto de líneas.
  2. utilizar triple comillas para generar una cadena multilínea: si bien este método es aceptado, tiene el inconveniente que, aunque no genera código ejecutable, el bloque delimitado no es ignorado por el intérprete, que crea el correspondiente objeto de tipo string.