Pular para o conteúdo
Editor

Argumentos de funções em Python: *args, **kwargs e valores padrão

Intermediário25 min7 exercícios105 XP

Este tutorial foi traduzido do original em inglês.Read in EnglishLeer en españolЧитать на русскомอ่านภาษาไทยAvisar sobre um erro de tradução

0/7 exercícios

Pense em pedir um café. Você sempre precisa escolher o tamanho: isso é obrigatório. Mas e os extras, como leite de aveia, uma dose extra de espresso ou calda de caramelo? Esses são opcionais. Você só fala deles quando quer.

Os argumentos de funções em Python funcionam do mesmo jeito. Alguns são obrigatórios, outros têm valores padrão sensatos, e alguns permitem passar quantos extras você quiser. Dominar esses padrões é o que separa um iniciante de alguém que escreve código realmente flexível.

Neste tutorial, você vai aprender todas as formas que o Python oferece para passar dados para funções, desde os argumentos posicionais básicos até os poderosos padrões *args e **kwargs, usados em bibliotecas do mundo real.

Argumentos posicionais versus argumentos nomeados

Quando você chama uma função, o Python associa os argumentos aos parâmetros de duas formas. Argumentos posicionais são associados pela posição: o primeiro argumento vai para o primeiro parâmetro. Argumentos nomeados (keyword arguments) são associados pelo nome.

Argumentos posicionais versus nomeados
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')
Saída
Whiskers is a cat
Buddy is a dog

Com argumentos nomeados, você escreve parameter_name=value. Isso deixa a chamada autoexplicativa: qualquer pessoa que leia o código vê exatamente o que cada valor significa, mesmo sem olhar a definição da função.

Você pode misturar os dois estilos na mesma chamada, mas os argumentos posicionais precisam vir antes dos nomeados.

Misturando posicionais e nomeados
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)
Saída
Whiskers is a 3-year-old cat

Como os valores padrão deixam as funções flexíveis

Valores padrão permitem definir parâmetros opcionais. Se quem chama a função fornece um valor, ele é usado. Se não, entra o valor padrão.

Valores padrão na prática
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)
Saída
large coffee
medium coffee with oat milk
small coffee with almond milk, 2 sugar(s)

O parâmetro size não tem valor padrão: ele é obrigatório. Os parâmetros milk e sugar têm valores padrão, então são opcionais. É exatamente assim que muitas bibliotecas Python reais funcionam: poucos parâmetros obrigatórios e muitos opcionais, com valores padrão sensatos.

A armadilha do valor padrão mutável

Essa é uma das pegadinhas mais famosas do Python. Quando você usa um objeto mutável (como uma lista) como valor padrão, esse objeto é criado uma única vez, quando a função é definida, e não a cada chamada da função.

Isso significa que todas as chamadas compartilham a mesma lista. As mudanças feitas em uma chamada passam para a próxima. Veja na prática:

A armadilha do valor padrão mutável
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'))
Saída
['apples']
['apples', 'bread']
['apples', 'bread', 'milk']

Surpreso? Cada chamada deveria começar com uma lista nova, mas a lista continua crescendo. Isso acontece porque [] é criada uma vez, no momento em que a função é definida, e reutilizada em todas as chamadas.

A correção é um padrão bem conhecido em Python: use None como valor padrão e crie uma lista nova dentro da função.

A correção: use None como valor padrão
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'))
Saída
['apples']
['bread']
['milk']

Para que serve o *args?

Às vezes você não sabe de antemão quantos argumentos uma função vai receber. Talvez você queira uma função que some qualquer quantidade de números: 2, 5 ou 100.

A sintaxe *args reúne todos os argumentos posicionais extras em uma tupla. Depois você pode percorrê-los, acessá-los por índice ou fazer com eles qualquer coisa que faria com uma tupla.

Usando *args para aceitar qualquer quantidade 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))
Saída
6
100
5

O nome args é só uma convenção. Você poderia escrever *numbers, *items ou *values. A mágica está no *: ele diz ao Python para empacotar todos os argumentos posicionais restantes em uma tupla.

Inspecionando *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)
Saída
<class 'tuple'>
('hello', 42, True)
Argument 0: hello
Argument 1: 42
Argument 2: True

Você pode combinar *args com parâmetros comuns. Os parâmetros comuns pegam seus valores primeiro, e depois *args reúne tudo o que sobrou.

Parâmetro comum + *args
def greet(greeting, *names):
for name in names:
print(greeting + ', ' + name + '!')
greet('Hello', 'Alice', 'Bob', 'Charlie')
Saída
Hello, Alice!
Hello, Bob!
Hello, Charlie!

Para que serve o **kwargs?

Enquanto *args reúne argumentos posicionais extras, **kwargs reúne argumentos nomeados extras em um dicionário. As chaves são os nomes dos argumentos, e os valores são os valores dos argumentos.

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

Assim como args, o nome kwargs é só uma convenção. Você poderia escrever **info, **options ou **settings. O que importa é o **: ele diz ao Python para empacotar os argumentos nomeados extras em um dicionário.

Isso é extremamente útil em funções que precisam ser flexíveis. Você pode passar quaisquer dados com nome sem mudar a definição da função.

Parâmetro comum + **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')
Saída
Username: alice_99
  email: alice@mail.com
  age: 25

Username: bob_42
  city: London

Combinando todos os tipos de argumento

Você pode usar parâmetros comuns, valores padrão, *args e **kwargs na mesma função. Mas a ordem importa. O Python exige esta sequência:

  • Parâmetros posicionais comuns
  • Parâmetros com valores padrão
  • *args
  • Parâmetros somente nomeados, como delivery=False no exemplo abaixo: depois de *args, eles só podem ser passados pelo nome
  • **kwargs
  • Todos os tipos de argumento 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')
    Saída
    Order from: Pizza Palace
    Items: margherita, garlic bread
    Delivery: True
      tip: 5
      note: extra cheese
    Flexível demais (difícil de entender)
    def process(*args, **kwargs):
        # What does this expect?
        pass
    Claro e explícito
    def process(data, format='json',
                verbose=False):
        # Easy to understand
        pass

    Exercícios práticos

    Preveja a chamada com argumentos nomeados
    Preveja a saída

    O que este código exibe? Digite a saída exata.

    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)
    Saudação com valor padrão
    Escreva o código

    Escreva uma função chamada greet que receba um parâmetro name e um parâmetro opcional greeting com valor padrão 'Hello'. Ela deve exibir <greeting>, <name>!. Por exemplo, greet('Alice') exibe Hello, Alice! e greet('Bob', 'Hey') exibe Hey, Bob!.

    # Define your function here
    # Test it
    greet('Alice')
    greet('Bob', 'Hey')
    Some todos os números
    Escreva o código

    Escreva uma função chamada sum_all que use *args para aceitar qualquer quantidade de argumentos numéricos e retorne a soma deles. Se nenhum argumento for passado, retorne 0.

    # Define your function here
    # Test it
    print(sum_all(1, 2, 3))
    print(sum_all(10, 20))
    print(sum_all())
    Corrija o valor padrão mutável
    Corrija o erro

    Esta função deveria criar uma lista nova a cada chamada, mas tem o bug do valor padrão mutável. Corrija-a para que cada chamada comece com uma lista nova.

    Saída 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))
    Monte uma string de perfil
    Escreva o código

    Escreva uma função chamada build_profile que receba um parâmetro obrigatório name e use **kwargs para aceitar qualquer informação adicional. Ela deve exibir o nome na primeira linha e depois cada par chave-valor nas linhas seguintes, no formato key: value (com dois espaços no início). Exiba as chaves na ordem em que foram passadas.

    Exemplo: build_profile('Alice', age=25, city='NYC') deve exibir:

    Alice
      age: 25
      city: NYC
    # Define your function here
    # Test it
    build_profile('Alice', age=25, city='NYC')
    Calculadora flexível
    Escreva o código

    Escreva uma função chamada calc que receba uma string operation ('add', 'multiply' ou 'max') e depois qualquer quantidade de argumentos numéricos usando *args. Ela deve retornar o resultado:

  • 'add': a soma de todos os números (0 se não houver nenhum)
  • 'multiply': o produto de todos os números (1 se não houver nenhum)
  • 'max': o maior número (0 se não houver nenhum)
  • Exemplos: calc('add', 1, 2, 3) retorna 6. calc('multiply', 2, 3, 4) retorna 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))
    Reescreva usando **kwargs
    Melhore o código

    Reescreva a função abaixo para usar **kwargs em vez de parâmetros individuais. A função deve aceitar name como parâmetro obrigatório e todo o resto por meio de **kwargs. Exiba o nome e depois cada detalhe em sua própria linha, no formato key: value. A saída precisa ser exatamente igual.

    Original:

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

    Saída 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)

    Tutoriais relacionados

    Este tutorial foi útil?