Las funciones son l贸gicas y fragmentos de c贸digo que pueden ejecutarse (o invocarse) repetidamente en distintos puntos de un programa, evitando la reiteraci贸n de c贸digo y dotando de mayor (valga la redundancia) funcionalidad al mismo. Su funcionamiento se basa en una serie de entradas, a las que llamamos argumentos, las cuales atraviesan un proceso (la ejecuci贸n del c贸digo de la funci贸n en s铆), para finalmente arrojar un resultado (la salida de la funci贸n). Adem谩s de permitirnos ahorrar escritura de c贸digo, gracias a estos argumentos, las funciones van m谩s all谩 de un "copiar-pegar" y pueden producir distintas salidas en funci贸n de los valores que les proporcionemos. La expectativa, es que siempre una misma funci贸n con los mismos par谩metros de entrada, sea capaz de producir la misma salida, dado que conocemos los pasos o c贸digo que se va ejecutando en su interior, haci茅ndola reproducible.
Estos valores otorgados depender谩n del contexto en el cual se est茅 ejecutando la funci贸n, y si buen pueden estar "redactados" en el mismo c贸digo, lo m谩s frecuente es que sean variables, que hayan capturado informaci贸n de diferentes medios durante la ejecuci贸n del programa.
Lo que nos interesa ver hoy es qu茅 ocurre en los casos que por cualquier motivo, la informaci贸n que est谩 esperando un argumento de la funci贸n no est谩 disponible, ya sea porque no fue capturada o no haya sido expl铆citamente definida por el programador o usuario.
Partamos de un caso b谩sico que nos resulte familiar, como por ejemplo la siguiente funci贸n:
def suma(num1, num2):
resultado = num1 + num2
return resultado
La funci贸n anterior toma dos argumentos (num1 y num2), los procesa en su interior para calcular la suma de ambos (en este caso, un proceso muy sencillo, pero dentro de una funci贸n pueden ocurrir procedimientos de distinta complejidad para llegar al resultado final), y devuelve un resultado, que posteriormente podremos imprimir o almacenar en una variable para su uso, por ejemplo, en otra funci贸n.
mi_resultado = suma(5,2)
print(f'El resultado de la suma es: {mi_resultado}')
En el caso anterior, para llegar a nuestro resultado, hemos proporcionado los dos argumentos que requiere la funci贸n suma() desde el momento que la hemos definido, es decir, dos valores (los ya mencionados num1, y num2). Una funci贸n tambi茅n podr铆a recibir una cantidad de argumentos distinta, o incluso una cantidad indeterminada de argumentos, pero lo siguiente es extrapolable a todos los casos:
¿Qu茅 ocurrir铆a, si por ejemplo, no contamos con ambos valores al momento de ejecutar una funci贸n que ha sido definida con un cierto n煤mero de argumentos? Retomando nuestra funci贸n anterior, ser铆a el caso por ejemplo de:
suma(5)
La ejecuci贸n devolver铆a un error, que se ver铆a m谩s o menos como:
TypeError: suma() missing 1 required positional argument: 'num2'
y que nos indica que nos hemos olvidado un argumento posicional: num2. Argumento posicional indica que cada uno de los valores que le hemos pasado a la funci贸n se incorporar谩 a la misma de acuerdo al orden o posici贸n con la que se los hemos pasado. Para la funci贸n suma() es irrelevante, pero por ejemplo para una funci贸n resta(), no:
def resta(num1,num2):
resultado = num1 - num2
return resultado
Para la funci贸n anterior, no es indiferente el orden en el que pasemos los argumentos, ya que por ejemplo resta(5,2) devolver谩 3, pero resta(2,5), devolver谩 -3. Para el caso de suma(), tanto suma(5,2) como suma(2,5) devolver谩n 7.
El orden no es lo 煤nico que determina la informaci贸n que recibe (o no) una funci贸n, y para el caso de las funciones, podemos pasar los argumentos de manera m谩s expl铆cita del siguiente modo:
suma(num1 = 5, num2 = 2)
Con lo cual, el resultado sigue siendo 7, pero hemos hecho algo m谩s expl铆cito el pasaje de la informaci贸n. Y si, retomando el ejemplo anterior, hici茅ramos
suma(num2 = 2)
Es decir, omitiendo otra vez un argumento, pero en esta oportunidad no el segundo (como ocurre de manera predeterminada), sino el primero (expl铆citamente), el error recibido ser铆a parecido pero diferente:
TypeError: suma() missing 1 required positional argument: 'num1'
En este caso, como habr谩s previsto, el argumento posicional faltante es num1.
En ciertas ocasiones, por ejemplo porque estemos trabajando con una funci贸n compleja que recibe gran cantidad de argumentos, pero que muchos de los cuales suelan ser casi siempre el mismo valor, puede ser algo extenso y agotador tener que declararlos a todos cada vez que se invoca la funci贸n.
En su lugar, lo l贸gico ser铆a preferir tener que declarar 煤nicamente los argumentos que necesitemos cambiar o controlar, y que el resto trabajen con valores predeterminados e iguales en cada oportunidad. Existe una alternativa que nos permite hacer eso, evitando el error "TypeError" que obten铆amos en los casos anteriores.
Pensemos una nueva funci贸n, por ejemplo, potencia(), la cual reciba tambi茅n dos argumentos: el primer argumento ser谩 la base de la potencia, y el segundo el exponente:
def potencia(base, exponente):
resultado = base ** exponente
return resultado
Podr铆amos plantear que, ante los casos que no se indique un valor en exponente, queremos que nuestra potencia trabaje como un cuadrado (potencia 2). Para permitir este tipo de comportamientos, tendremos que asignarle este valor predeterminado al exponente:
def potencia(base, exponente = 2):
resultado = base ** exponente
return resultado
¿Esto quiere decir que exponente siempre valdr谩 2 al invocar a la funci贸n? Desde luego que no (para ello directamente no crear铆amos el argumento), sino que lo que se implica, es que cada vez que se invoque a la funci贸n, si no se indica un valor para exponente, al mismo ser谩 asignado el valor 2. De esa manera:
print(potencia(2,3))
>> 8
print(potencia(5))
>> 25
Y con lo cual, a pesar de haber brindado 煤nicamente un par谩metro a la funci贸n potencia(), el resultado queda determinado en lugar de arrojar un error.
El caso
print(potencia(exponente=3))
A煤n arrojar谩 un error TypeError, dado que base no tiene valor predeterminado asignado.
Vale la pena prestar especial atenci贸n al proceso realizado: para asignar un valor a un argumento posicional, lo asignamos mediante el nombre de dicho argumento y el signo = al invocar la funci贸n. Pero para asignar un argumento predeterminado, lo hacemos del mismo modo, pero al definir la funci贸n. Esta diferencia es fundamental.
Resulta importante destacar que nuestros argumentos predeterminados no pueden ser seguidos por par谩metros sin su valor predeterminado, siguiendo la estructura al definir una funci贸n:
- Primero, declarar todos los argumentos no predeterminados (argumento)
- Luego, los argumentos predeterminados (argumento = valor)
- Finalmente, los argumentos indefinidos (*args y **kwargs)
Como comentario final, el objetivo de un argumento predeterminado no es cubrir "olvidos" a la hora de invocar nuestras funciones: en todos los casos tendremos que ser cuidadosos y precisos con el c贸digo para poder de esa manera obtener los resultados esperados. M谩s bien, el objetivo es permitirnos ahorrar tiempo y ganar simplicidad, a la hora de invocar funciones cuando las mismas tienen un n煤mero significativo de argumentos, entre los cuales solo unos pocos nos resultan prioritarios.