2.1. Funciones#

2.1.1. Enlaces#

2.1.2. Funciones con parámetros y retorno#

def funcion_1(x):
    """
    Esto se llama en inglés "docstring". Sirve para explicar lo que hace la función, 
    cualquier información que pueda ser útil al usuario y a quién la haya creado
    
    x: número (real o entero)
    función_1: valor de x**2 - 1
    """
    resultado = x**2 -1
    return resultado
funcion_1(2)
3
funcion_1()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Input In [3], in <cell line: 1>()
----> 1 funcion_1()

TypeError: funcion_1() missing 1 required positional argument: 'x'
funcion_1
<function __main__.funcion_1(x)>
type(funcion_1(2.))
float
def funcion_2(a,b,c):
    """
    a,b,c: números
    funcion_2: suma de cuadrados de a, b y c
    """
    return a**2 + b**2 + c**2
funcion_2(1,2,1)
6
def funcion_3(cadena):
    """
    cadena: str
    funcion_3: devuelve True o False según tenga cadena más de tres caracteres o no
    """
    if len(cadena) > 3:
        return True
    else:
        return False
funcion_3('estudios')
True
type(funcion_3('estudios'))
bool
def funcion_4(palabra, letra):
    """
    palabra: str, cadena de texto
    letra: str, una letra 
    funcion_4: devuelve una nueva palabra donde se han cambiado las vocales por letra
    """
    rsltd = ''
    vocales = 'aeiouáéíóúüAEIOUÁÉÍÚÜ'
    for s in palabra:
        if s in vocales:
            rsltd += letra
        else:
            rsltd += s     
    return rsltd 
funcion_4('barco','s')
'bsrcs'
type(funcion_4('barco','s'))
str

2.1.3. Funciones sin paramétros y sin retorno#

def funcion_5():
    """
    Imprime '¡Hola Mundo!'
    """
    print('¡Hola Mundo!')
funcion_5()
¡Hola Mundo!
a = funcion_5()
¡Hola Mundo!
a
type(a)
NoneType
def funcion_6():
    """
    Dibuja un rectángulo con asteriscos
    """
    for i in range(8):
        print('* '*8)
funcion_6()
* * * * * * * * 
* * * * * * * * 
* * * * * * * * 
* * * * * * * * 
* * * * * * * * 
* * * * * * * * 
* * * * * * * * 
* * * * * * * * 
type(funcion_6())
* * * * * * * * 
* * * * * * * * 
* * * * * * * * 
* * * * * * * * 
* * * * * * * * 
* * * * * * * * 
* * * * * * * * 
* * * * * * * * 
NoneType

2.1.4. Funciones sin paramétros y con retorno#

def funcion_7():
    """
    devuelve ¡Hola!
    """
    rsltd = '¡Hola!'
    return rsltd
funcion_7()
'¡Hola!'
type(funcion_7())
str

2.1.5. Ámbito de las variables definidas dentro de funciones#

Las variables definidas dentro de una función solo tienen existencia en el espacio de nombres de esa función

def funcion_8():
    i = 1
    j = i + 1
    print(j)
funcion_8()
2
i
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Input In [27], in <cell line: 1>()
----> 1 i

NameError: name 'i' is not defined
j
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Input In [28], in <cell line: 1>()
----> 1 j

NameError: name 'j' is not defined

Cuando se invoca una función se crea un espacio de nombres propio de esa función. De esta forma no se produce ningún problema si en el interior de varias funciones hemos utilizado el mismo nombre para variables que se comportan de forma distinta en cada función.

Si un variable se utiliza dentro de una función y no se ha inicializado dentro de la función, antes de producir un error, Python busca fuera del espacio de nombres de la función para ver si está definida en el espacio de nombres que se abre cuando se lanza Python. Podríamos llamar a éste, el espacio general de nombres de Python. Observad que cada vez que se cierra Python se cierra este espacio general de nombres y todos los espacios de nombres generados cada vez que se invoca una función. Al abrir Python de nuevo se genera un nuevo espacio general de nombres, que no hereda nada del anterior, ya que es el espacio de la anterior sesión ha desaparecido junto con todos los espacios de nombres que se hayan generado cada vez que se invoca una función.

En resumen: Una variable definida en el cuerpo de una función solo tiene existencia en el espacio de nombres de esa función. Pero todas las funciones pueden usar variables del espacio de nombres general de Python, siempre y cuando no cambien su valor.

En el caso de abajo no hay problema

a = 3

def funcion_9():
    b = a + 1
    print(b)
funcion_9()
4
a
3
b
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Input In [32], in <cell line: 1>()
----> 1 b

NameError: name 'b' is not defined

Pero en este caso de abajo hay un problema ya que no se puede modificar el valos de a en el cuerpo de la función:

a = 5

def funcion_10():
    a = a + 1
    return a
funcion_10()
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
Input In [34], in <cell line: 1>()
----> 1 funcion_10()

Input In [33], in funcion_10()
      3 def funcion_10():
----> 4     a = a + 1
      5     return a

UnboundLocalError: local variable 'a' referenced before assignment

Sin embargo en el ejemplo de abajo no hay problema ya que hay una variable b en el espacio de la función que por lo tanto es diferente del la variable b del espacio generla de nombres:

b = 5

def funcion_11(x):
    b = x
    return x + 1
funcion_11(2)
3
b
5

Si definimos una variable dentro de una función, no estará disponible fuera:

def funcion_12(x,y):
    aux = x % y
    return aux
funcion_12(13,5)
3
aux
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Input In [40], in <cell line: 1>()
----> 1 aux

NameError: name 'aux' is not defined

Si queremos que la variable aux esté disponible fuera de la función funcion_12 debemos definirla como variable global:

def funcion_13(x,y):
    global aux
    aux = x % y
    return aux
funcion_13(13,5)
3
aux
3

Otros cuatro ejemplos para ilustrar el ámbito de las variables definidas dentro de una función. Primer caso:

def f(y):
    x = 1
    x += 1
    print(x)
x = 5 
f(x) 
print(x)
2
5

Segundo caso:

def g(y):
    print(x)
    print(x + 1)
x = 5 
g(x) 
print(x)
5
6
5

Tercer caso:

def h(x):
    x = x + 1
x = 5 
h(x) 
print(x)
5

Cuarto caso:

def k(x):
    x = x + 1
    y = y + 1
    return x, y
x = 5 
y = 1
k(x) 
print(x)
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
Input In [51], in <cell line: 3>()
      1 x = 5 
      2 y = 1
----> 3 k(x) 
      4 print(x)

Input In [50], in k(x)
      1 def k(x):
      2     x = x + 1
----> 3     y = y + 1
      4     return x, y

UnboundLocalError: local variable 'y' referenced before assignment

2.1.6. Uso de una función en el cuerpo de otra#

2.1.6.1. Ejemplos#

Ejemplo 1

def u(x):
    if x != 0:
        return x**3 + 2*x
    else:
        return 5

def w_1(x):
    return x + u(x)
w_1(1)
4

Ejemplo 2

Ahora definimos una solo función en cuyo cuerpo hay otra función. Vermos que estos dos procedimientos dan el mismo resultado.

def w_2(x):
    def u(x):
        if x != 0:
            return x**3 + 2*x
        else:
            return 5
    return x + u(x)
w_2(1)
4

Las funciones anteriores w_1() y w_2() hacen lo mismo

for n in range(-7, 7):
    if w_1(n) != w_2(n):
        print('Horror')
    else:
        print('Todo va bien')
Todo va bien
Todo va bien
Todo va bien
Todo va bien
Todo va bien
Todo va bien
Todo va bien
Todo va bien
Todo va bien
Todo va bien
Todo va bien
Todo va bien
Todo va bien
Todo va bien
def comprobación(f, g):
    """
    Con esto podemos comprobar que las dos funciones devuelven los mismos valores 
    si los parámetros toman como valores, número enteros
    """
    diferentes = False
    for n in range(-20, 20):
        if f(n) != g(n):
            diferentes = True
            break
    if diferentes:
         print('Horror')
    else:
        print('Todo va bien')
    
comprobación(w_1, w_2)
Todo va bien

2.1.6.2. Composición de funciones#

2.1.6.2.1. Ejemplo#

def f(x):
    return x + 1

def g(x):
    return 3*x
g(f(2))
9
f(3 + g(-1))
1

2.1.7. Parámetros: orden, posición y valores#

El orden en que se escriben los parámetros se puede cambiar

Definimos la siguiente función que depende de tres parámetros:

def subintervalos(N,a,b):
    """
    N: int
    a,b: float o int
    funcion_12: sin retorno. Divide el intervalo (a,b) en N subintervalos iguales y 
                los imprime
    """
    h = (b-a)/N
    for k in range(N):
        print('(' + str(a + h*k) + ',' + str(a + h*(k+1)) + ')')
subintervalos(20,1,2)
(1.0,1.05)
(1.05,1.1)
(1.1,1.15)
(1.15,1.2)
(1.2,1.25)
(1.25,1.3)
(1.3,1.35)
(1.35,1.4)
(1.4,1.45)
(1.45,1.5)
(1.5,1.55)
(1.55,1.6)
(1.6,1.65)
(1.65,1.7000000000000002)
(1.7000000000000002,1.75)
(1.75,1.8)
(1.8,1.85)
(1.85,1.9)
(1.9,1.9500000000000002)
(1.9500000000000002,2.0)

Cuando se utilizan los parámetros de una función sin indicar su nombre se habla de parámetros posicionales ( positional parameters ). Si variamos de orden los valores (20, 1, 2) obtendremos cosas distintas al invocar la función.

También se puede escribir

subintervalos(b = 2, N = 20, a = 1)
(1.0,1.05)
(1.05,1.1)
(1.1,1.15)
(1.15,1.2)
(1.2,1.25)
(1.25,1.3)
(1.3,1.35)
(1.35,1.4)
(1.4,1.45)
(1.45,1.5)
(1.5,1.55)
(1.55,1.6)
(1.6,1.65)
(1.65,1.7000000000000002)
(1.7000000000000002,1.75)
(1.75,1.8)
(1.8,1.85)
(1.85,1.9)
(1.9,1.9500000000000002)
(1.9500000000000002,2.0)

Cuando se utilizan los parámetros de una función indicando su nombre se habla de parámetros referenciales ( keywords arguments ). De esta forma podemos utilizar cualquier orden para introducir los parámetros.

2.1.7.1. Parámetros con valores por defecto#

En la siguiente función, el parámetro N toma el valor 20 por defecto. Son los parámetros por defecto

def subintervalos_p(a,b, N = 20):
    """
    N: int
    a,b: float o int
    funcion_12: sin retorno. Divide el intervalo (a,b) en N subintervalos iguales y 
                los imprime
    """
    h = (b-a)/N
    for k in range(N):
        print('(' + str(a + h*k) + ',' + str(a + h*(k+1)) + ')')

Esto significa que si solo indicamos el valor de dos parámetros, Python le dará el valor 20 a N. Los otros dos parámetros se asignarán por orden, el primero a a y el segundo a b.

subintervalos_p(1,2)
(1.0,1.05)
(1.05,1.1)
(1.1,1.15)
(1.15,1.2)
(1.2,1.25)
(1.25,1.3)
(1.3,1.35)
(1.35,1.4)
(1.4,1.45)
(1.45,1.5)
(1.5,1.55)
(1.55,1.6)
(1.6,1.65)
(1.65,1.7000000000000002)
(1.7000000000000002,1.75)
(1.75,1.8)
(1.8,1.85)
(1.85,1.9)
(1.9,1.9500000000000002)
(1.9500000000000002,2.0)

Pero también podemos suministrar un valor específico para N mediante el uso referenciales del parámetro. Por ejemplo:

subintervalos_p(1,2,N = 10)
(1.0,1.1)
(1.1,1.2)
(1.2,1.3)
(1.3,1.4)
(1.4,1.5)
(1.5,1.6)
(1.6,1.7000000000000002)
(1.7000000000000002,1.8)
(1.8,1.9)
(1.9,2.0)

Como ya hemos visto, los parámetros como a y b son posicionales y N sería un parámetro referencial. De esta forma hemos cambiado el valor por defecto de N.

Los parámetros referenciales siempre deben de ir al final. En caso contrario tendremos un error

subintervalos_p(1,N=10,2)
  Input In [68]
    subintervalos_p(1,N=10,2)
                           ^
SyntaxError: positional argument follows keyword argument
subintervalos_p(N=10, 1, 2)
  Input In [69]
    subintervalos_p(N=10, 1, 2)
                          ^
SyntaxError: positional argument follows keyword argument

2.1.8. Ejemplos#

2.1.8.1. Ejemplo#

Escribe la función saludo() que devuelve “Buenos dias”

def saludo1():
    return "Buenos días"
saludo1()
'Buenos días'

2.1.8.2. Ejemplo#

Escribe la función saludo2(x) que devuelve “Buenos dias x”, donde x es una cadena

def saludo2(x):
    s="Buenos días "+x
    return s
saludo2("Luis")
'Buenos días Luis'

2.1.8.3. Ejemplo#

Escribe la función metroscentimetros(x) devuelve x metros en centímetros donde x es un número

def metroscentimetros(x):
    return x*100
metroscentimetros(15)
1500

2.1.8.4. Ejemplo#

Escribe la función centigrkelvin(x) que devuelve xºC en ºK donde x es un número

def centigrkelvin(x):
    return x+273.15
centigrkelvin(0)
273.15

2.1.8.5. Ejemplo#

Escribe la función long(x) que cuenta los caracteres de una cadena x

def long(x):
    contador=0
    for i in x:
        contador+=1
    return contador    
long("mi cadena")
9

2.1.8.6. Ejemplo#

Escribe la función reemplazar(texto, letra, nletra) que reemplaza el caracter letra por nletra en la cadena texto

def reemplazar(texto, letra, nletra):
    ntexto=""
    for l in texto:
        if l==letra:
            ntexto+=nletra
        else:
            ntexto+=l
    return ntexto
reemplazar("mi cadena",nletra="***",letra="a")
'mi c***den***'
reemplazar(saludo1(),nletra="***",letra="a")
'Buenos dí***s'

2.1.8.7. Ejemplo#

Escribe la función sinacentos1(texto) que devuelve una cadena de letras minúsculas sin acentos. La variable texto es una cadena de letras minúsculas.

def sinacentos1(texto):
    ntexto=""
    for letra in texto:
        if letra=="á":
            ntexto+="a"
        elif letra=="é":
            ntexto+="e"
        elif letra=="í":
           ntexto+="i"
        elif letra=="ó":
           ntexto+="o"
        elif letra=="ú":
            ntexto+="u"
        else:
            ntexto+=letra
    return  ntexto
sinacentos1("adéúdé")
'adeude'

Otra versión de sinacentos(texto)

def sinacentos2(texto): 
    conAcentos = 'áéíóúü'
    sinAcentos = 'aeiouu'
    for i in range(len(conAcentos)):
        nuevo_texto = ''
        for s in texto:
            #print(i,s)
            if s == conAcentos[i]:
                nuevo_texto += sinAcentos[i]
            else:
                nuevo_texto += s
            #print(nuevo_texto)
        texto=nuevo_texto
    return texto
sinacentos2("además")
'ademas'

La última versión de sinacentos(texto) utilizando la función reemplazar() que creamos más arriba

def sinacentos3(texto):
    st="áéíóú"
    s="aeiou"
    for i in range(5):
        if st[i] in texto:
           texto = reemplazar(texto,st[i],s[i])
    return texto
sinacentos3("además")
'ademas'

2.1.8.8. Ejemplo#

Ámbito de una variable: Utilizamos una variable x en la función g, en la función h y en el programa principal ¿qué ocurre?

Caso 1

def g1(x):
    def h():
        x = 'abc'
    x = x + 1
    print('En g1(x): x =', x)
    h()
    return x
x = 3
z = g1(x)
En g1(x): x = 4
x
3
z
4

Caso 2

def g2(x):
    def h():
        x = 'abc'
        return x
    x = x + 1
    print('En g2(x): x =', x)
    return h()
x = 3
z = g2(x)
En g2(x): x = 4
x
3
z
'abc'

Ponemos un print() en el cuerpo de la función h() para ver que está pasando

def g3(x):
    def h():
        x = 'abc'
        print('En h(): x =', x)
        return x
    x = x + 1
    print('En g3(x): x =', x)
    return h()
z = g3(3)
En g3(x): x = 4
En h(): x = abc
x
3
z
'abc'

2.1.8.9. Ejemplo#

Escribe la función bisiesto(año) que devuelve True o False si el año es bisiesto o no, donde año es un entero

def bisiesto(año):
    """
    año - número que corresponde a un año
    Devuelve True o False si el año es bisiesto o no
    """
    return (año%4 == 0)
bisiesto(2020)
True
bisiesto(2019)
False

2.1.8.10. Ejemplo#

Escribe la función dias(año) que devuelve el número de días de un año. La variable año es entera.

def dias(año):
    if bisiesto(año):
        return 366
    else:
        return 365
dias(2020)
366
dias(2019)
365
def bisiesto(año):
    """
    año - número que corresponde a un año
    Devuelve True o False si el año es bisiesto o no
    """
    return (año%4 == 0)

def dias(año):
    if bisiesto(año):
        return 366
    else:
        return 365
def dias(año):
    def bisiesto(año):
        """
        año - número que corresponde a un año
        Devuelve True o False si el año es bisiesto o no
        """
        return (año%4 == 0)
    if bisiesto(año):
        return 366
    else:
        return 365

2.1.8.11. Ejemplo#

Escribe la función mayor(nombre1,edad1,nombre2,edad2) que compara edad1 y edad2 e imprime lo siguiente:

Ejemplos:

mayor('Juan',18, 'Ana', 15) imprime Juan es 3 años mayor que Ana

mayor('Juan',18, 'Ana', 18) imprime Juan y Ana tienen la misma edad

def mayor(nombre1,edad1,nombre2,edad2):
    if edad1 > edad2:
        print(nombre1 + ' es ' + str(edad1 - edad2) + ' años mayor que ' + nombre2)
    elif edad2 > edad1:
        print(nombre2 + ' es ' + str(edad2 - edad1) + ' años mayor que ' + nombre1)
    else:
        print(nombre1 + ' y ' + nombre2 + ' tienen la misma edad')
mayor('Juan',24, 'Ana', 24)
Juan y Ana tienen la misma edad

2.1.8.12. Ejemplo#

Escribe la función primer_elemento(I) que devuelve el primer elemento del iterable I

def primer_elemento(I):
    """
    I - iterable arbitrario
    devuelve el primer elemento de I
    """
    for u in I:
        return u
I = 'ahora'
primer_elemento(I)
'a'
primer_elemento(range(8,24))
8

2.1.8.13. Ejemplo#

Escribe la función suma(m,n) que devuelve la suma de los números enteros de m a n ambos inclusive. En caso de que m sea mayor que n no debe devolver nada.

def suma(m,n):
    if m <= n:
        suma = 0
        for u in range(m, n + 1):
            suma += u
        return suma
suma(1,3)
6
suma(3,1)

2.1.8.14. Ejemplo#

Escriba la función juego() que escoge un número entre 1 y 6, ambos inclusive, pero no nos lo dice. Nos pide un número por teclado y si es correcto imprime Has acertado. En caso contrario, imprime No has acertado. Prueba otra vez y nos da la posiblidad de volver a decir un número.

Aquí se puede utilizar la función randint() que no es una función propia de Python, pertence al módulo random. Esta función proporciona números aleatorios.

Después define la función juego(N) que inicia un juego como el de antes para los números del \(1\) al \(N,\) ambos inclusive.

def juego():
    from random import randint # así podemos utilizar la funcíon randint() del módulo random
    N = randint(1,6)
    
    while True:
        n = int(input('Di un número entre 1 y 6 :'))
        if n == N:
            print('Has acertado')
            break
        else:
            print('No has acertado. Prueba otra vez')
            print()
juego()
def Juego(K):
    from random import randint
    N = randint(1,K)
    
    while True:
        n = int(input('Di un número entre 1 y ' + str(K) + ': '))
        if n == N:
            print('Has acertado')
            break
        else:
            print('No has acertado. Prueba otra vez')
            print()
Juego(10)

2.1.9. Ejercicios#

Definir las siguientes funciones que realizan las tareas indicadas a continuación:

  1. bienvenida(): imprime el mensaje Bienvenido a Python.

  2. f(x): devuelve el valor de \(x^3 - 3x^2 + 1\) para un número n.

  3. potencia_numero(n,m): devuelve \(n\) elevado a \(m,\) donde \(n\) y \(m\) son enteros.

  4. suma_digitos(n): devuelve la suma de los dígitos del número entero n.

    • Ejemplo: la llamada suma_digitos(631) devuelve el entero 10.

  5. compara(x,y): devuelve \(0\) si \(x=y\), \(1\) si \(x>y\), \(-1\) si \(x<y\).

    • Ejemplo: la llamada compara(3,2)devuelve 1

  6. punto_medio(a,b): devuelve el punto medio del intervalo \((a,b)\).

  7. semintervalo_izq(a,b): devuelve el semintervalo (mitad) izquierdo del intervalo \((a,b)\).

    • Ejemplo: la llamada semintervalo_izquierdo(0,2)devuelve (0,1)

  8. semintervalo_der(a,b): devuelve el semintervalo (mitad) derecho del intervalo \((a,b)\).

  9. pertenece(N,a,b): devuelve True o False según \(N\) pertenezca o no al intervalo \((a,b)\).

  10. divide_mitad(N,a,b): devuelve el semintervalo de \((a,b)\) donde se encuentra \(N\). Si \(N\) no pertenece al intervalo, devuelve N no está en el intervalo.

    • Ejemplo: la llamada divide_mitad(17, 0, 100) devuelve (0,50)

    • Ejemplo: la llamada divide_mitad(65, 0, 100) devuelve (50,100)

    • Ejemplo: la llamada divide_mitad(165, 0, 100) devuelve 165 no etá en el intervalo

  11. zoom(palabra): devuelve la palabra en mayúsculas y con sus letras separadas por un espacio.

    • Ejemplo: la llamada zoom(‘sandwich’) devuelve la cadena S A N D W I C H

  12. mayor2(a,b): devuelve el máximo de los números \(a,b\).

  13. mayor3(a,b,c): devuelve el máximo de los números \(a,b,c\).

  14. maximo4(a,b,c,d) que devuelve el máximo de los números \(a, b, c\) y \(d.\)

  15. rectangulo(n,m): dibuja un rectángulo con el símbolo 0 de ancho \(n\) y alto \(m,\) donde \(n\) y \(m\) son enteros.

  16. factorial(n): devuelve el factorial del entero no negativo \(n\).

  17. alfa(cadena): devuelve una cadena en la que se han cambiado las tres primeras letras a de cadena por A.

  18. suma_cuadrados_2(a,b,c,d): devuelve la suma de los cuadrados de los dos números más grandes entre \(a,b,c,d\).

    • Ejemplo: la llamada suma_cuadrados_2(2,4,1,6) devuelve 52 (\(= 4^2 + 6^2\)).

  19. frecuencia(texto,letra): devuelve el número de veces que aparece letra en texto.

    • Ejemplo: la llamada frecuencia(‘vaca’, ‘a’) devuelve 2.

    • Ejemplo: la llamada frecuencia(‘vaca’, ‘q’) devuelve 0.

  20. esta_ordenado(cadena): devuelve True o False según las letras de cadena estén ordenados alfabéticamente o no, suponiendo que las letras de cadena son minúsculas y no tienen tildes.

  21. mi_max(cadena): devuelve el mayor de los caracteres de cadena que está formada por letras minúsculas sin acentos.

    • Ejemplo: la llamada mi_max('una vaca asturiana') devuelve v

  22. sin_tildes(cadena): devuelve una cadena que corresponde a quitar las tildes de las vocales de cadena.

    • Ejemplo: la llamada sin_tildes('un relámpago iluminó el cielo') devuelve un relampago ilumino el cielo

  23. letra_mas_frecuente(cadena): devuelve la letra más frecuente en el texto cadena.

  24. dinamica(n,f,a): devuelve el \(n\)-ésimo término de la sucesión definida por

\[ x_0 = a, \quad, x_{n+1} = f(x_n),\]

donde \(a\) es un número real y \(f\) una función.

2.1.10. Cuestionarios de Moodle#

Funciones 1

Funciones 2

Funciones 3