Fundamentos
Parámetros y argumentos en Python: posicionales, con nombre, valores por defecto y *args
Aprende a pasar valores a funciones en Python: parámetros y argumentos, posición y nombre, valores por defecto, *args y las trampas más comunes.
Por Prof. Rodrigo Faustino · Actualizado el · 15 min de lectura
Métricas de este artículo
- Palabras
- 2801
- Caracteres
- 15.779
- Tiempo de lectura
- ~15 min
Parámetro es el nombre que la función declara en el encabezado, y argumento es el valor que la llamada pone en ese nombre. Los argumentos pueden pasarse por posición, en el orden en que se escribieron los parámetros, o por nombre, con parametro=valor. Un parámetro puede tener valor por defecto, que vale cuando la llamada no manda nada, y un parámetro con asterisco, como *args, recoge en una tupla todos los argumentos que sobren.
En el artículo anterior las funciones tenían los paréntesis vacíos, como saudar(), y hacían siempre lo mismo. Con parámetros, la misma función trabaja con valores diferentes en cada llamada: parpadear tres veces o dos, saludar a Ana o a Rui, sumar tres números o cinco. Este artículo muestra las cuatro formas de pasar valores, qué cambia en el cuerpo de la función, los errores que provoca cada forma y cómo aparece esto en programas de ESP32.
Parámetro y argumento
Los dos nombres confunden a mucha gente porque describen la misma conexión vista desde lados distintos. El parámetro vive en la definición, entre los paréntesis del def, y es un nombre para un valor que todavía no existe. El argumento vive en la llamada, y es el valor de verdad. Cuando ocurre la llamada, Python une cada argumento con un parámetro, y dentro de la función el parámetro se comporta como una variable que ya empieza con el valor recibido.
def piscar(vezes, pausa):
print("parpadear", vezes, "veces con pausa de", pausa)
piscar(3, 0.5)
piscar(2, 0.1)
v = 5
p = 0.25
piscar(v, p)La salida es parpadear 3 veces con pausa de 0.5, parpadear 2 veces con pausa de 0.1 y parpadear 5 veces con pausa de 0.25. El argumento puede ser un número escrito en el momento, como en las dos primeras llamadas, o una variable, como en la tercera. El nombre de la variable de la llamada no tiene que ser el mismo del parámetro: v y p llegan dentro de la función como vezes y pausa.
Una consecuencia que suele sorprender: cambiar el parámetro dentro de la función no cambia la variable de afuera.
def dobrar(n):
n = n * 2
print("dentro", n)
n = 5
dobrar(n)
print("fuera", n)
x = 7
dobrar(x)
print("x sigue", x)La salida es dentro 10, fuera 5, dentro 14 y x sigue 7. El n de adentro y el n de afuera tienen el mismo nombre por coincidencia y son variables diferentes. En la llamada, la función recibió el valor 5 y creó su propio n, que desapareció cuando la función terminó. El artículo sobre alcance detalla las reglas. Por ahora, quédate con que pasar un valor no le da a la función el poder de alterar la variable original.
Del programa al semestre planificado
- Plan de enseñanza y calendario montados solos
- Diario de clases listo para pegar en el SIGA
- Diapositivas y listas de ejercicios con solucionario
Versión Community gratis · Windows
Argumentos posicionales
La manera más simple de pasar valores es por la posición: el primer argumento va al primer parámetro, el segundo al segundo, y así sucesivamente.
def intervalo(inicio, fim):
print("de", inicio, "hasta", fim)
intervalo(2, 10)
intervalo(10, 2)La salida es de 2 hasta 10 y de 10 hasta 2. La segunda llamada no da error, y ese es el problema: con los argumentos cambiados, el programa corre y entrega un resultado equivocado. Con parámetros de tipos diferentes, el error suele aparecer enseguida, pero con dos números el cambio pasa desapercibido.
def dividir(a, b):
print(a, "/", b, "=", a / b)
dividir(10, 4)
dividir(4, 10)La salida es 10 / 4 = 2.5 y 4 / 10 = 0.4. Cuando el orden importa, como en la división, vale la pena comprobar la definición antes de llamar. En funciones con cuatro o cinco parámetros, la posición deja de ser confiable de memoria, y ahí entran los argumentos con nombre.
Argumentos con nombre
En la llamada, el argumento puede venir acompañado del nombre del parámetro, y entonces la posición ya no decide nada:
def intervalo(inicio, fim):
print("de", inicio, "hasta", fim)
intervalo(2, 10)
intervalo(fim=10, inicio=2)
intervalo(2, fim=10)Las tres llamadas dan de 2 hasta 10. En la segunda, los dos argumentos tienen nombre y el orden es libre. En la tercera, el primero va por posición y el segundo por nombre, lo que está permitido siempre que los posicionales vengan antes que los que tienen nombre.
Vale usar el nombre en la llamada cuando el valor solo no explica nada. piscar(3, 0.5, 4) obliga al lector a abrir la definición para saber qué son 3, 0.5 y 4. En cambio, piscar(vezes=3, pausa=0.5, pino=4) se lee sola. Ya lo haces con funciones listas, como en print("a", "b", sep="-"): el sep es un parámetro con nombre del print, igual que el end.
Existen tres errores típicos con nombres. El primero es dar el mismo parámetro dos veces, por posición y por nombre:
def piscar(vezes, pausa=0.2):
print(vezes, pausa)
piscar(3, vezes=2)Traceback (most recent call last):
File "main.py", line 4, in <module>
TypeError: function got multiple values for argument 'vezes'El 3 fue a vezes por posición, y el vezes=2 intentó llenar el mismo lugar. El segundo es usar un nombre que la función no tiene:
def piscar(vezes, pausa=0.2):
print(vezes, pausa)
piscar(3, tempo=1)Traceback (most recent call last):
File "main.py", line 4, in <module>
TypeError: unexpected keyword argument 'tempo'El tercero es poner un argumento posicional después de uno con nombre, lo que Python rechaza antes incluso de ejecutar:
def piscar(vezes, pausa=0.2):
print(vezes, pausa)
piscar(vezes=3, 0.5)Traceback (most recent call last):
File "main.py", line 4
SyntaxError: positional arg after keyword argEl Python del computador dice positional argument follows keyword argument y el del simulador usa la forma corta. En los tres casos la lectura es la misma: el error está en la línea de la llamada, y la solución es mirar la definición.
Valores por defecto
Un parámetro puede tener un valor por defecto, escrito con = en el encabezado. Si la llamada no manda nada para él, el valor por defecto entra en su lugar:
def saudar(nome, saudacao="hola"):
print(saudacao + ",", nome)
saudar("Ana")
saudar("Rui", "buenos días")
saudar("Bia", saudacao="buenas noches")
saudar(saudacao="ey", nome="Leo")La salida es hola, Ana, buenos días, Rui, buenas noches, Bia y ey, Leo. El nome es obligatorio, y la saudacao es opcional. En la primera llamada vale el valor por defecto, en las otras dos se cambia, por posición y por nombre, y en la última los dos argumentos van por nombre, en el orden inverso.
La tabla de la figura muestra la misma función, piscar(vezes, pausa=0.2, pino=2), en seis llamadas. piscar(3) usa dos valores por defecto. piscar(3, 0.5) cambia el segundo valor por posición. piscar(3, pino=4) salta la pausa y cambia solo el pin, que es la gran ventaja del argumento con nombre: permite alterar un valor por defecto del medio o del final sin repetir los que vienen antes. Las dos últimas filas son errores, el primero por falta de vezes, que no tiene valor por defecto, y el segundo por vezes repetido.
def piscar(vezes, pausa=0.2, pino=2):
print("veces", vezes, "pausa", pausa, "pin", pino)
piscar(3)
piscar(3, 0.5)
piscar(3, pino=4)
piscar(pausa=0.1, vezes=2)
piscar(1, 0.3, 5)La salida es veces 3 pausa 0.2 pin 2, veces 3 pausa 0.5 pin 2, veces 3 pausa 0.2 pin 4, veces 2 pausa 0.1 pin 2 y veces 1 pausa 0.3 pin 5, una línea para cada llamada.
Hay una regla de orden en la definición: los parámetros obligatorios van antes que los que tienen valor por defecto. Escribir lo contrario es un error de sintaxis:
def piscar(pausa=0.2, vezes):
print(vezes, pausa)Traceback (most recent call last):
File "main.py", line 1
SyntaxError: non-default argument follows default argumentLa razón es práctica. En una llamada como piscar(3), Python no tendría cómo saber si el 3 es la pausa o las veces. Poniendo los obligatorios primero, la posición siempre tiene respuesta.
Un ejemplo del día a día es el precio con cantidad y descuento opcionales:
def total(preco, qtd=1, desconto=0):
print(preco * qtd - desconto)
total(10)
total(10, 3)
total(10, 3, 5)
total(10, desconto=2)
total(10, 3, desconto=5)La salida es 10, 30, 25, 8 y 25. Sin opcionales, la función vale para el caso más común, y quien necesita más control manda solo lo que cambia.
La IA que recuerda tu vida y vive en tu ordenador
- Memoria a largo plazo con la fuente de cada respuesta
- Funciona sin conexión: tus datos se quedan en tu PC
- Lee PDF, diapositivas, fotos y grabaciones de reuniones
Plan Esencial gratis para siempre
Cantidad variable con *args
A veces no se sabe cuántos valores vendrán: sumar tres notas, cinco lecturas o dos. Un parámetro con asterisco antes del nombre recoge en una tupla todos los argumentos posicionales que sobren:
def somar(*numeros):
print(numeros, type(numeros))
total = 0
for n in numeros:
total = total + n
print("suma", total)
somar(4, 7, 9)
somar(5)
somar()
lista = [1, 2, 3]
somar(*lista)La salida tiene cuatro bloques de dos líneas. Para somar(4, 7, 9), muestra (4, 7, 9) <class 'tuple'> y suma 20. Para somar(5), (5,) <class 'tuple'> y suma 5. Para somar(), () <class 'tuple'> y suma 0. Para somar(*lista), (1, 2, 3) <class 'tuple'> y suma 6. El cuerpo de la función trata numeros como un grupo cualquiera y usa un for, como en los artículos sobre bucles. La tupla de un solo elemento se escribe (5,), con la coma, y la tupla vacía, (), se acepta: llamar sin ningún argumento no es un error.
El asterisco tiene dos sentidos, según el lugar donde aparece. En la definición, reúne: *numeros junta los argumentos extra en una tupla. En la llamada, esparce: somar(*lista) entrega los elementos de la lista como si cada uno se hubiera escrito por separado, somar(1, 2, 3). La palabra args es solo la convención, y *numeros o *notas funcionan igual, con el nombre que mejor explique el contenido.
Se pueden combinar parámetros fijos con el asterisco. Los fijos van antes y reciben los primeros valores. Lo que sobre va a la tupla:
def media(primeiro, *resto):
soma = primeiro
for n in resto:
soma = soma + n
print("media", soma / (1 + len(resto)), "de", 1 + len(resto))
media(8)
media(8, 6)
media(8, 6, 7)La salida es media 8.0 de 1, media 7.0 de 2 y media 7.0 de 3. Aquí el primeiro es obligatorio. Llamar media() sin nada daría TypeError, lo que es deseable: el promedio de cero valores no tiene sentido, y la función se niega a calcular.
Después del *resto, un parámetro solo puede llenarse por nombre. Esto es útil para opciones que no deben confundirse con los valores del grupo:
def unir(*partes, sep="-"):
texto = ""
for i in range(len(partes)):
if i > 0:
texto = texto + sep
texto = texto + str(partes[i])
print(texto)
unir("a", "b", "c")
unir("a", "b", "c", sep="/")
unir(1, 2)
print("a", "b", "c", sep="-")
print("x", "y", end="!")
print()La salida es a-b-c, a/b/c, 1-2, a-b-c y x y!. El print que usas desde el comienzo de la serie está construido con la misma idea: acepta cualquier número de valores y tiene los parámetros con nombre sep y end. Una consecuencia es que el * recoge solo argumentos sueltos, y no un grupo entero dentro de una lista:
def somar(*numeros):
print(sum(numeros))
somar([1, 2, 3])Traceback (most recent call last):
File "main.py", line 4, in <module>
File "main.py", line 2, in somar
TypeError: unsupported types for __add__: 'int', 'list'La llamada pasó un solo argumento, la lista, y numeros pasó a ser ([1, 2, 3],), una tupla con una lista dentro. El sum intentó sumar 0 con la lista y falló. El arreglo es esparcir con el asterisco en la llamada, somar(*[1, 2, 3]), o llamar sin lista, somar(1, 2, 3). El Python del computador escribe el mensaje como unsupported operand type(s) for +: 'int' and 'list'.
Existe todavía una versión para los argumentos con nombre que sobran, con dos asteriscos, y el nombre que se usa por convención es **kwargs. Reúne los argumentos con nombre en un diccionario:
def mostrar(**dados):
print(dados)
for chave in dados:
print(chave, "=", dados[chave])
mostrar(nome="Ana", idade=20)
mostrar()La salida es {'nome': 'Ana', 'idade': 20}, nome = Ana, idade = 20 y {}. Es menos común en programas didácticos. Sirve para funciones que pasan opciones a otras, y basta saber que existe.
En la ESP32
En hardware, los valores por defecto vuelven cómodas las funciones. Los datos que casi nunca cambian, como la pausa y la cantidad de parpadeos, quedan como valor por defecto, y quien necesita otro valor manda solo ese. El pin, que cambia de montaje en montaje, es el primer parámetro y es obligatorio:
from machine import Pin
import time
def piscar(pino, vezes=3, pausa=0.1):
led = Pin(pino, Pin.OUT)
for i in range(vezes):
led.on()
time.sleep(pausa)
led.off()
time.sleep(pausa)
print("pin", pino, "parpadeos", vezes)
piscar(2)
piscar(2, 2)
piscar(4, vezes=1, pausa=0.05)El Serial muestra pin 2 parpadeos 3, pin 2 parpadeos 2 y pin 4 parpadeos 1. La función crea el Pin con el número que recibió, así que la misma función sirve para cualquier LED. El Pin(pino, Pin.OUT) es también una llamada con dos argumentos posicionales, y el segundo, Pin.OUT, es el modo del pin. Leer la documentación de una función lista es, en buena parte, leer la lista de parámetros y descubrir cuáles tienen valor por defecto. En los artículos sobre PWM, ADC e I2C aparecen otras funciones y clases listas, y vale la pena comprobar, en cada una, qué argumentos son obligatorios y cuáles tienen valor por defecto.
Trampas que Python no acusa
Los errores vistos hasta aquí avisan al instante. Dos casos no avisan.
El valor por defecto que recuerda la llamada anterior
El valor por defecto se crea una vez, cuando corre el def, y no en cada llamada. Con números y textos, que no se alteran en el lugar, esto no aparece. Con una lista, aparece:
def acumular(x, lista=[]):
lista.append(x)
print(lista)
acumular(1)
acumular(2)
acumular(3)
print("---")
def acumular2(x, lista=None):
if lista is None:
lista = []
lista.append(x)
print(lista)
acumular2(1)
acumular2(2)La salida es [1], [1, 2], [1, 2, 3], la separación y después [1] y [2]. En la primera versión, la lista del valor por defecto es siempre la misma, y cada llamada le agrega elementos. La segunda usa None como señal de "no mandaron nada" y crea la lista dentro de la función, lo que da una lista nueva en cada llamada. La regla: nunca uses lista, diccionario o conjunto vacío como valor por defecto. Usa None y crea adentro.
Argumentos en el orden equivocado
Dos variables del mismo tipo intercambiadas de lugar producen un resultado equivocado y ningún mensaje. Lo que ayuda es poner nombres: dividir(a=4, b=10) documenta la intención, y intervalo(inicio=2, fim=10) no deja dudas.
Cuenta equivocada de argumentos
Sin los nombres, el error que más aparece es el TypeError de conteo. Los dos sentidos son:
def piscar(vezes, pausa=0.2):
print(vezes, pausa)
piscar()Traceback (most recent call last):
File "main.py", line 4, in <module>
TypeError: function takes 1 positional arguments but 0 were givendef piscar(vezes, pausa=0.2):
print(vezes, pausa)
piscar(1, 2, 3)Traceback (most recent call last):
File "main.py", line 4, in <module>
TypeError: function takes 2 positional arguments but 3 were givenEl Python del computador es más claro: piscar() missing 1 required positional argument: 'vezes' en el primer caso y piscar() takes from 1 to 2 positional arguments but 3 were given en el segundo. El sentido es el mismo, faltó o sobró un argumento, y la línea señalada es la de la llamada.
En el aula: dónde suelen tropezar los parámetros
Estos casos se repiten en los grupos.
Parámetro y argumento tratados como la misma cosa
Cuando el alumno dice "el parámetro 5", mezcló los dos. La pregunta de comprobación: ¿esto está en el def o en la llamada? En el def es parámetro, en la llamada es argumento.
Leer el valor con input dentro de la función
En lugar de recibir el número como parámetro, la función llama a input() dentro de ella. Funciona una vez y no sirve para nada más: no se puede llamar con un valor que ya existe, ni probar sin escribir. La función recibe los datos, y quien lee el input es el programa principal.
Usar una variable de afuera en lugar de recibir un parámetro
La función usa largura y altura que están en el programa, sin recibir nada. Corre, hasta que alguien cambia el nombre de la variable de afuera y la función se rompe, o intenta usarla en dos rectángulos diferentes. La señal de alerta es una función que solo funciona si existe una variable con un nombre específico fuera de ella.
Cambiar el parámetro y esperar que cambie la variable de afuera
Es el caso del dobrar(n): el alumno ve cambiar el valor dentro de la función, mira la variable de afuera y cree que el computador "se olvidó". El parámetro es una copia del valor, como mostró el ejemplo.
Poner el valor por defecto antes del obligatorio
El error de sintaxis es claro, pero el alumno suele "arreglarlo" quitando el valor por defecto y dejando los dos obligatorios, y pierde el beneficio. La solución es reordenar: obligatorios primero.
Lista como valor por defecto
El caso del acumular. La primera prueba funciona, la segunda muestra un resultado "sucio" que el alumno atribuye a un defecto del computador. Quien ya pasó por esto una vez recuerda el None para siempre.
Todo en *args
Quien acaba de descubrir el asterisco pasa a escribir def f(*args) en todo, y la función pierde el contrato: nadie sabe cuántos valores espera, ni qué significan. El *args es para cuando la cantidad varía de verdad.
Ejercicio: tabla de multiplicar y promedio con opciones
Escribe una función tabuada(n, ate=10) que muestre la tabla de multiplicar de n de 1 hasta ate, y una función mostrar_media(*notas) que muestre el promedio de las notas recibidas, o sin notas si no llega ninguna.
def tabuada(n, ate=10):
for i in range(1, ate + 1):
print(n, "x", i, "=", n * i)
tabuada(3, 4)
print("---")
tabuada(2, ate=3)
print("---")
def mostrar_media(*notas):
if len(notas) == 0:
print("sin notas")
return
print("media", sum(notas) / len(notas))
mostrar_media(8, 6, 7)
mostrar_media()La salida es la tabla del 3 de 1 a 4, la del 2 de 1 a 3, media 7.0 y sin notas. El return sin valor, en la función del promedio, solo termina la función antes de tiempo; el artículo siguiente trata del return completo. Para mejorarlo, haz que tabuada acepte también un inicio=1 y, en el promedio, piensa qué debería ocurrir si alguien manda una nota negativa.
Preguntas frecuentes
¿Cuál es la diferencia entre parámetro y argumento?
Parámetro es el nombre en la definición, argumento es el valor en la llamada. En la práctica, las dos palabras se mezclan en la conversación, y lo que importa es saber de qué lado de la conexión se está hablando.
¿Puedo mezclar posicionales y con nombre en la misma llamada?
Puedes, siempre que los posicionales vengan primero. piscar(3, pino=4) funciona, y piscar(vezes=3, 0.5) es un error de sintaxis.
¿Por qué no se escribe espacio alrededor del = en los valores por defecto?
Es una convención de estilo de Python: pausa=0.2 y sep="-" sin espacios, tanto en la definición como en la llamada. Funciona con espacios, pero fuera de la costumbre.
¿El nombre tiene que ser args?
No. Lo que vale es el asterisco. El nombre args es costumbre, y un nombre mejor, como notas, deja el código más legible.
¿Cuántos parámetros debe tener una función?
No existe un número mágico, pero las funciones con más de cuatro o cinco parámetros suelen estar haciendo demasiadas cosas. Cuando eso ocurra, vale preguntar si la función no debería dividirse en dos.
¿*args y **kwargs funcionan en la ESP32?
Funcionaron en el simulador, y MicroPython sigue la misma sintaxis de Python en este punto. Los mensajes de error de conteo de argumentos son más cortos que los del computador.
Para continuar
Para practicar, el desafío Área del Rectángulo pide la función area(largura, altura) con dos argumentos posicionales, y la Calculadora con Funciones pide dos funciones que reciben a y b. Las dos usan return, que es el tema del próximo artículo, pero la parte de los parámetros ya se puede leer ahora. Si quieres repasar la base, vuelve al artículo sobre funciones y def, y la ruta Lógica con Python reúne todos los desafíos.
El próximo artículo trata del return: cómo la función devuelve un valor a quien la llamó, cómo devolver más de uno y por qué print y return no son lo mismo.