Saltar al contenido
Editor

Argumentos en Python: *args, **kwargs y valores predeterminados

Intermedio25 min7 ejercicios105 XP

Esta lección es una traducción del original en inglés.Read in EnglishAvisar de un error de traducción

0/7 ejercicios

Piensa en cuando pides un café. Siempre tienes que elegir un tamaño: eso es obligatorio. ¿Pero los extras, como leche de avena, un espresso extra o un chorrito de caramelo? Esos son opcionales. Solo los mencionas cuando los quieres.

Los argumentos de las funciones de Python funcionan igual. Algunos son obligatorios, otros tienen valores predeterminados razonables y otros te dejan pasar tantos extras como quieras. Dominar estos patrones es lo que distingue a un principiante de alguien que escribe código realmente flexible.

En este tutorial aprenderás todas las formas en que Python te deja pasar datos a las funciones, desde los argumentos posicionales básicos hasta los potentes patrones *args y **kwargs que se usan en las bibliotecas reales.

Argumentos posicionales y argumentos con nombre

Cuando llamas a una función, Python asigna los argumentos a los parámetros de dos maneras. Los argumentos posicionales se asignan según su posición: el primer argumento va al primer parámetro. Los argumentos con nombre (keyword arguments) se asignan por su nombre.

Argumentos posicionales y con nombre
def describe_pet(animal, name):
print(name + ' is a ' + animal)
# Positional: order matters
describe_pet('cat', 'Whiskers')
# Keyword: order doesn't matter
describe_pet(name='Buddy', animal='dog')
Salida
Whiskers is a cat
Buddy is a dog

Con los argumentos con nombre, escribes parameter_name=value. Así la llamada se explica sola: cualquiera que lea el código ve exactamente qué significa cada valor, sin tener que mirar la definición de la función.

Puedes mezclar los dos estilos en la misma llamada, pero los argumentos posicionales deben ir antes que los argumentos con nombre.

Mezclar argumentos posicionales y con nombre
def describe_pet(animal, name, age):
print(name + ' is a ' + str(age) + '-year-old ' + animal)
# Positional first, then keyword
describe_pet('cat', name='Whiskers', age=3)
Salida
Whiskers is a 3-year-old cat

Cómo los valores predeterminados hacen flexibles las funciones

Los valores predeterminados te permiten definir parámetros opcionales. Si quien llama a la función da un valor, se usa ese. Si no, entra en juego el valor predeterminado.

Valores predeterminados en acción
def make_coffee(size, milk='regular', sugar=0):
order = size + ' coffee'
if milk != 'regular':
order += ' with ' + milk + ' milk'
if sugar > 0:
order += ', ' + str(sugar) + ' sugar(s)'
print(order)
make_coffee('large')
make_coffee('medium', 'oat')
make_coffee('small', 'almond', 2)
Salida
large coffee
medium coffee with oat milk
small coffee with almond milk, 2 sugar(s)

El parámetro size no tiene valor predeterminado: es obligatorio. Los parámetros milk y sugar sí lo tienen, así que son opcionales. Así funcionan muchas bibliotecas reales de Python: unos pocos parámetros obligatorios y muchos opcionales con valores predeterminados razonables.

La trampa de los valores predeterminados mutables

Esta es una de las sorpresas más famosas de Python. Cuando usas un objeto mutable (como una lista) como valor predeterminado, ese objeto se crea una sola vez, al definir la función, y no cada vez que se llama.

Eso significa que todas las llamadas comparten la misma lista. Los cambios de una llamada pasan a la siguiente. Veámoslo en acción:

La trampa del valor predeterminado mutable
def add_item(item, shopping_list=[]):
shopping_list.append(item)
return shopping_list
print(add_item('apples'))
print(add_item('bread'))
print(add_item('milk'))
Salida
['apples']
['apples', 'bread']
['apples', 'bread', 'milk']

¿Te sorprende? Cada llamada debería empezar con una lista nueva, pero la lista no para de crecer. Esto pasa porque [] se crea una sola vez, cuando se define la función, y se reutiliza en cada llamada.

La solución es un patrón muy conocido de Python: usa None como valor predeterminado y crea la lista nueva dentro de la función.

La solución: usar None como valor predeterminado
def add_item(item, shopping_list=None):
if shopping_list is None:
shopping_list = []
shopping_list.append(item)
return shopping_list
print(add_item('apples'))
print(add_item('bread'))
print(add_item('milk'))
Salida
['apples']
['bread']
['milk']

¿Qué hace *args?

A veces no sabes de antemano cuántos argumentos va a recibir una función. Quizá quieres una función que sume cualquier cantidad de números: 2, 5 o 100.

La sintaxis *args reúne todos los argumentos posicionales extra en una tupla. Luego puedes recorrerlos, acceder a ellos por índice o hacer con ellos cualquier cosa que harías con una tupla.

Usar *args para aceptar cualquier número de argumentos
def add_all(*numbers):
total = 0
for n in numbers:
total += n
return total
print(add_all(1, 2, 3))
print(add_all(10, 20, 30, 40))
print(add_all(5))
Salida
6
100
5

El nombre args es solo una convención. Podrías escribir *numbers, *items o *values. La magia está en el *: le dice a Python que empaquete en una tupla todos los argumentos posicionales que quedan.

Inspeccionar *args
def show_args(*args):
print(type(args))
print(args)
for i, arg in enumerate(args):
print('Argument ' + str(i) + ': ' + str(arg))
show_args('hello', 42, True)
Salida
<class 'tuple'>
('hello', 42, True)
Argument 0: hello
Argument 1: 42
Argument 2: True

Puedes combinar *args con parámetros normales. Los parámetros normales toman sus valores primero y luego *args recoge todo lo que sobra.

Parámetro normal + *args
def greet(greeting, *names):
for name in names:
print(greeting + ', ' + name + '!')
greet('Hello', 'Alice', 'Bob', 'Charlie')
Salida
Hello, Alice!
Hello, Bob!
Hello, Charlie!

¿Qué hace **kwargs?

Mientras que *args reúne los argumentos posicionales extra, **kwargs reúne los argumentos con nombre extra en un diccionario. Las claves son los nombres de los argumentos y los valores son sus valores.

Usar **kwargs
def build_profile(**info):
for key, value in info.items():
print(key + ': ' + str(value))
build_profile(name='Alice', age=25, city='Paris')
Salida
name: Alice
age: 25
city: Paris

Igual que args, el nombre kwargs es solo una convención. Podrías escribir **info, **options o **settings. Lo que importa es el **: le dice a Python que empaquete en un diccionario los argumentos con nombre extra.

Esto es muy útil para funciones que necesitan ser flexibles. Puedes pasar cualquier dato con nombre sin cambiar la definición de la función.

Parámetro normal + **kwargs
def create_user(username, **details):
print('Username: ' + username)
for key, value in details.items():
print(' ' + key + ': ' + str(value))
create_user('alice_99', email='alice@mail.com', age=25)
print()
create_user('bob_42', city='London')
Salida
Username: alice_99
  email: alice@mail.com
  age: 25

Username: bob_42
  city: London

Combinar todos los tipos de argumentos

Puedes usar parámetros normales, valores predeterminados, *args y **kwargs en la misma función. Pero el orden importa. Python exige este orden concreto:

  • Parámetros posicionales normales
  • Parámetros con valores predeterminados
  • *args
  • **kwargs
  • Todos los tipos de argumentos juntos
    def order_food(restaurant, *items, delivery=False, **extras):
    print('Order from: ' + restaurant)
    print('Items: ' + ', '.join(items))
    print('Delivery: ' + str(delivery))
    if extras:
    for key, value in extras.items():
    print(' ' + key + ': ' + str(value))
    order_food('Pizza Palace', 'margherita', 'garlic bread',
    delivery=True, tip=5, note='extra cheese')
    Salida
    Order from: Pizza Palace
    Items: margherita, garlic bread
    Delivery: True
      tip: 5
      note: extra cheese
    Demasiado flexible (difícil de entender)
    def process(*args, **kwargs):
        # What does this expect?
        pass
    Claro y explícito
    def process(data, format='json',
                verbose=False):
        # Easy to understand
        pass

    Ejercicios de práctica

    Predice la salida: argumentos con nombre
    Predice la salida

    ¿Qué imprime este código? Escribe la salida exacta.

    def power(base, exponent=2):
        print(base ** exponent)
    
    power(5)
    power(2, 10)
    power(exponent=3, base=4)
    def power(base, exponent=2):
    print(base ** exponent)
    power(5)
    power(2, 10)
    power(exponent=3, base=4)
    Saludo con un valor predeterminado
    Escribe el código

    Escribe una función llamada greet que reciba un parámetro name y un parámetro opcional greeting con el valor predeterminado 'Hello'. Debe imprimir <greeting>, <name>!. Por ejemplo, greet('Alice') imprime Hello, Alice! y greet('Bob', 'Hey') imprime Hey, Bob!.

    # Define your function here
    # Test it
    greet('Alice')
    greet('Bob', 'Hey')
    Suma todos los números
    Escribe el código

    Escribe una función llamada sum_all que use *args para aceptar cualquier cantidad de argumentos numéricos y devuelva su suma. Si no recibe ningún argumento, debe devolver 0.

    # Define your function here
    # Test it
    print(sum_all(1, 2, 3))
    print(sum_all(10, 20))
    print(sum_all())
    Corrige el valor predeterminado mutable
    Corrige el error

    Esta función debería crear una lista nueva cada vez, pero tiene el error del valor predeterminado mutable. Corrígela para que cada llamada empiece con una lista nueva.

    Salida esperada:

    [1]
    [2]
    [3]
    def append_to(value, items=[]):
    items.append(value)
    return items
    print(append_to(1))
    print(append_to(2))
    print(append_to(3))
    Construye un perfil en texto
    Escribe el código

    Escribe una función llamada build_profile que reciba un parámetro obligatorio name y use **kwargs para aceptar cualquier información adicional. Debe imprimir el nombre en la primera línea y luego cada par clave-valor en las líneas siguientes, con el formato key: value (con dos espacios al principio). Imprime las claves en el orden en que se pasan.

    Ejemplo: build_profile('Alice', age=25, city='NYC') debe imprimir:

    Alice
      age: 25
      city: NYC
    # Define your function here
    # Test it
    build_profile('Alice', age=25, city='NYC')
    Calculadora flexible
    Escribe el código

    Escribe una función llamada calc que reciba una cadena operation ('add', 'multiply' o 'max') y luego cualquier cantidad de argumentos numéricos mediante *args. Debe devolver el resultado:

  • 'add': la suma de todos los números (0 si no hay ninguno)
  • 'multiply': el producto de todos los números (1 si no hay ninguno)
  • 'max': el número más grande (0 si no hay ninguno)
  • Ejemplos: calc('add', 1, 2, 3) devuelve 6. calc('multiply', 2, 3, 4) devuelve 24.

    # Define your function here
    # Test it
    print(calc('add', 1, 2, 3))
    print(calc('multiply', 2, 3, 4))
    print(calc('max', 5, 2, 8, 1))
    Refactoriza para usar **kwargs
    Mejora el código

    Refactoriza la función de abajo para que use **kwargs en lugar de parámetros individuales. La función debe aceptar name como parámetro obligatorio y todo lo demás a través de **kwargs. Imprime el nombre y luego cada dato en su propia línea con el formato key: value. La salida debe coincidir exactamente.

    Original:

    def show_student(name, grade, school, gpa):
        print(name)
        print('grade: ' + str(grade))
        print('school: ' + school)
        print('gpa: ' + str(gpa))

    Salida esperada para show_student('Alice', grade=10, school='Lincoln', gpa=3.8):

    Alice
    grade: 10
    school: Lincoln
    gpa: 3.8
    def show_student(name, grade, school, gpa):
    print(name)
    print('grade: ' + str(grade))
    print('school: ' + school)
    print('gpa: ' + str(gpa))
    show_student('Alice', grade=10, school='Lincoln', gpa=3.8)

    Tutoriales relacionados

    ¿Te sirvió este tutorial?

    Sugerir una mejora