Funciones#

En la clase pasada vimos los fundamentos del lenguaje: palabras reservadas, tipos de variables y estructuras de control de flujo. Hoy vamos a ver un concepto fundamental en todo lenguaje de programación: las funciones. Las funciones permiten estructurar el código de un programa en unidades que se ocupan de una tarea particular.

Ya vimos una función importante, que es la que me permite ver el resultado de un programa: la función print.

Las funciones reciben una cierta cantidad de datos de entrada, denominados argumentos de la función, y pueden devolver (o no) uno o varios resultados.

Para definir funciones, usamos la palabra reservada def. Empecemos definiendo una función parecida a print:

# Definición (o declaración) de la función
def saluda():
    
    print('Chau mundo!')
    

Hemos definido la función saluda que no recibe ningún argumento. La función tiene como nombre saluda, y no recibe ningún argumento, de allí los paréntesis vacíos (). Nótese también la indentación, que es obligatoria también en este caso, al igual que en las estructuras de control.

¿Cómo se llama, es decir, se ejecuta, a la función?

# Ejecutamos la función saluda()
saluda()
Chau mundo!
def saluda():
    
    print('Hola mundo!')
    
saluda()
Hola mundo!
# Definimos la función
def saluda_a(amigo):
    
    print('Hello, ', amigo," !")

# Ejecutamos la función    
saluda_a("John")
saluda_a(3)
Hello,  John  !
Hello,  3  !
mi_amigo = "Carlos"
saluda_a(mi_amigo)
Hello,  Carlos  !

No puedo llamar a esta función con más argumentos:

lennon = "John"
mccartney = "Paul"
saluda_a(lennon,mccartney)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[6], line 3
      1 lennon = "John"
      2 mccartney = "Paul"
----> 3 saluda_a(lennon,mccartney)

TypeError: saluda_a() takes 1 positional argument but 2 were given

Ni con menos:

saluda_a()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-11-279f2a3e3b85> in <module>
----> 1 saluda_a()

TypeError: saluda_a() missing 1 required positional argument: 'amigo'

Las funciones pueden retornar valores, se usa la palabra reservada return:

# x es el argumento formal de la función
def al_cuadrado(x): 
    """ Esta función eleva al cuadrado
        el valor de entrada"""
    z = 3 
    print("z adentro de al_cuadrado:", z)
    y = x**2
    # Valor de salida de la función 
    return y
    
    
a = 2
# a es el argumento real de la función
b = al_cuadrado(a)
y = 5
print(a," al cuadrado es:", b)
print("y afuera de al_cuadrado:", y)
print("z afuera de al_cuadrado:", z)
z adentro de al_cuadrado: 3
2  al cuadrado es: 4
y afuera de al_cuadrado: 5
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-17-a1068c403541> in <module>
     14 print(a," al cuadrado es:", b)
     15 print("y afuera de al_cuadrado:", y)
---> 16 print("z afuera de al_cuadrado:", z)

NameError: name 'z' is not defined
c = 9
d = al_cuadrado(c)
print(d)

Varias cosas importantes a notar

  • Puedo definir variables dentro de una función. De hecho, cualquier cosa que se pueda hacer en Python, se puede hacer dentro de una función

  • En la expresión b = al_cuadrado(a) asignamos el resultado de la función a la variable b.

  • x se denomina argumento formal de la función

  • a es el argumento real de la función, cuando la usamos en b = al_cuadrado(a). Es decir, que cuando la función se ejecuta, el argumento formal x adopta el valor del argumento real a. Esto es exactamente lo mismo que cuando en matemática hacemos $\(f(x) = x^2\)\( y dada \)a=23\(, queremos calcular \)f(a)\(. Simplemente reemplazamos el valor de \)a\( en \)x$ en la definición de la función.

Hay que estar atento al tipo de argumento

john = "Lennon"
al_cuadrado(john)
z adentro de al_cuadrado: 3
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-18-8ec7c819cbb0> in <module>
      1 john = "Lennon"
----> 2 al_cuadrado(john)

<ipython-input-17-a1068c403541> in al_cuadrado(x)
      4     z = 3
      5     print("z adentro de al_cuadrado:", z)
----> 6     y = x**2
      7     # Valor de salida de la función
      8     return y

TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'
def al_cubo(x):
    
    y = x*x*x
    
    return y

def ala_cuarta(x):
    
    y = x**4
    
    return y

b = al_cubo(3)
print(b)
b = ala_cuarta(b)
print(b)
27
531441
def al_cubo_no_devuelve_nada(x):
    
    y = x*x*x
   
    # acá falta un return y....
    
b = al_cubo_no_devuelve_nada(4)
print(b)
None

Ejemplos#

La secuencia de Fibonacci#

Una de las secuencias más importantes en matemática (y en el arte!) es la secuencia de Fibonacci: $\( f_0 = 0\)\( \)\( f_1 = 1\)\( \)\( f_n = f_{n-1} + f_{n-2}\)$

La secuencia de Fibonacci es responsable del número de oro: $\(\phi = \lim_{n\rightarrow \infty} \frac{f_n}{f_{n-1}} = \frac{1 + \sqrt{5}}{2}\)$

def fib(n):
    """Devuelve una lista con los términos
    de la serie de Fibonacci hasta n."""
    
    result = [0,1]
    i = 2
    
    while i < n:
        fn = result[i-1] + result[i-2]
        result.append(fn)
        print("iteración: ",i, "sec: ",result)
        i = i + 1
        
    return result
#
# Arreglar fib(n) para que fib(0) de como
# resultado 0
#

fib(0)
[0, 1]

Múltiples argumentos de entrada y salida#

def divide_seguro(x,y):
    """ Calcula x/y, pero devuelve un
        error si y es cero"""
    if(y==0):
        return None,"Error: y vale 0"
    else:
        return x/y, "Ok"

a = 8.2
b = 0
    
cociente, result = divide_seguro(a,b)
print("cociente: ",cociente,", result:",result)

c = 2.5
cociente, result = divide_seguro(a,c)
print("cociente: ",cociente,", result:",result)
    
cociente:  None , result: Error: y vale 0
cociente:  3.28 , result: Ok

Un poco de ayuda de mis amigos#

La función help nos da ayuda sobre las funciones, ya sean del lenguaje o las que programamos, si es que tienen el docstring adecuado.

help(print)
Help on built-in function print in module builtins:

print(...)
    print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
    
    Prints the values to a stream, or to sys.stdout by default.
    Optional keyword arguments:
    file:  a file-like object (stream); defaults to the current sys.stdout.
    sep:   string inserted between values, default a space.
    end:   string appended after the last value, default a newline.
    flush: whether to forcibly flush the stream.
help(fib)
Help on function fib in module __main__:

fib(n)
    Devuelve una lista con los términos
    de la serie de Fibonacci hasta n.
help(divide_seguro)
Help on function divide_seguro in module __main__:

divide_seguro(x, y)
    Calcula x/y, pero devuelve un
    error si y es cero

Ahora vamos al TP2…

Argumentos opcionales#

def caida_libre(t, h0, v0 = 0., g=9.8):
    """
    Devuelve la velocidad y la posición de una partícula en
    caída libre para condiciones iniciales dadas
    
    """
    v = v0 - g*t
    h = h0 - v0*t - g*t**2/2.
  
    return v, h

v1, h1 = caida_libre(10, 1000)

print(v1)
print(h1)

v1, h1 = caida_libre(10, 1000, g = 12)

print(v1)
print(h1)

Ámbito de las variables#

def func1(x):
    print('x entró a la función con el valor', x)
    x = 2
    print('El nuevo valor de x es', x)

y = 50
print('Originalmente x vale',y)
func1(y)
print('Ahora x vale',y)  
x = [50]
print('Originalmente x vale',x)

def func2(x):
    print('x entró a la función con el valor', x)
    x = [2]
    print('El nuevo valor de x es', x)

func2(x)
print('Ahora x vale',x) 
x = [50]
print('Originalmente x vale',x)

def func3(x):
    print('x entró a la función con el valor', x)
    x[0] = 2
    print('El nuevo valor de x es', x)

func3(x)
print('Ahora x vale',x)  
x = [50]
print('Originalmente x vale',x)

def func3(x):
    print('x entró a la función con el valor', x)
    x = 'lala'
    print('El nuevo valor de x es', x)

func3(x)
print('Ahora x vale',x)  
x = [1,2,3,4,5]
print('Originalmente x vale',x)

def func3(x):
    print('x entró a la función con el valor', x)
    x[2] = 108
    print('El nuevo valor de x es', x)

func3(x)
print('Ahora x vale',x)  

Algo de I/O#

Con las siglas I/O se hace referencia usualmente a los procedimientos de entrada y salida (del inglés, Input/Output). Esto se refiere la mayor parte de las veces a leer o escribir datos, ya sea de pantalla, archivo, etc.

Ya vimos la función print que escribe en la pantalla. Su contrapartida es la función input, que permite leer datos por pantalla:

algo_ingresado = input('Ingrese algo: ')

print(algo_ingresado)
print("El tipo de dato ingresado es:",type(algo_ingresado))
Ingrese algo: 3
3
El tipo de dato ingresado es: <class 'str'>

Al igual que la función print, que siempre escribe un tipo string, input siempre recibe el tipo string.

Cargar y escribir archivos de texto#

Otra operación fundamental de I/O es la carga de datos desde archivos, y su escritura:

writeme = open('ejemplo.txt','w',encoding = 'utf-8') # 'w' es write, es decir, escribir

texto = 'Esta es la primer línea,\nesta es la segunda\ny esta es la tercera.'

writeme.write(texto)

writeme.close()

Codificaciones de texto: ASCII UTF-8 etc. etc.

readme = open('ejemplo.txt') 

texto_leido = readme.read()

print(texto_leido)

readme.close()
Esta es la primer línea,
esta es la segunda
y esta es la tercera,
y le agrego una cuarta línea.
readme = open('ejemplo.txt') 

texto_leido = readme.read(16)

print(texto_leido)

mas_texto_leido = readme.read()

print(mas_texto_leido)

readme.close()
Esta es la prime
r línea,
esta es la segunda
y esta es la tercera,
y le agrego una cuarta línea.
readme = open('ejemplo.txt')

for linea in readme:
    print(linea)
    
readme.close() 
Esta es la primer línea,

esta es la segunda

y esta es la tercera,

y le agrego una cuarta línea.
readme = open('ejemplo.txt')
lineas = readme.readlines()

print(type(lineas))
print(len(lineas))
print(lineas[0])
print(lineas)
<class 'list'>
4
Esta es la primer línea,

['Esta es la primer línea,\n', 'esta es la segunda\n', 'y esta es la tercera,\n', 'y le agrego una cuarta línea.']