Vamos criar uma função simples que recebe dois argumentos inteiros, printa na tela cada um deles e ao final soma os dois:
>>> def soma_dois(x, y):
print(f'x = {x}')
print(f'y = {y}')
return x + y
>>> soma_dois(5, 7)
x = 5
y = 7
12
>>> soma_dois(y=10, x=3)
x = 3
y = 10
13
Percebam que ao passar os argumentos eu posso dizer explicitamente que quero que y seja 10 e x seja 3. Dessa forma, estou nomeando os argumentos. Na primeira chamada da função não nomeei os argumentos então o Python recebeu os dois valores e atribuiu x e y na ordem dada pela definição da função.
Entretanto, vamos tentar chamar a função passando só um argumento:
>>> soma_dois(5)
Traceback (most recent call last):
File "<pyshell#8>", line 1, in <module>
soma_dois(5)
TypeError: soma_dois() missing 1 required positional argument: 'y'
Percebam que a mensagem de erro informou que à chamada da função faltou informar um argumento posicional, que era o "y". Então, eu criei uma função que recebe dois argumentos posicionais, x e y. Vou refatorar a função só pra adicionar uma docstring informando que a função recebe dois parâmetros posicionais.
>>> def soma_dois_posicionais(x, y):
"""
x e y são parâmetros posicionais
"""
print(f'x = {x}')
print(f'y = {y}')
return x + y
>>> soma_dois_posicionais(6, 3)
x = 6
y = 3
9
>>> soma_dois_posicionais(y=55, x=3)
x = 3
y = 55
58
?O resultado é o mesmo. A única coisa que fiz foi adicionar a docstring para explicar melhor a função.
Entretanto, como dito acima, essa primeira versão da função recebe dois parâmetros posicionais. Outra forma de passar os parâmetros na definição da função é nomear esses parâmetros. Vamos criar uma nova versão dessa função nomeando os parâmetros:
>>> def soma_dois_nomeados(x=10, y=20):
"""
x e y são parâmetros nomeados, ou seja, na falta de um ou outro, o valor default será usado
"""
print(f'x = {x}')
print(f'y = {y}')
return x + y
>>> soma_dois_nomeados()
x = 10
y = 20
30
>>> soma_dois_nomeados(5)
x = 5
y = 20
25
>>> soma_dois_nomeados(5, 10)
x = 5
y = 10
15
>>> soma_dois_nomeados(y=666, x=333)
x = 333
y = 666
999
Notem o comportamento nas chamadas da função agora. Se eu não passar nenhum argumento, a função soma os dois valores default, 10 e 20. Se eu passar só um sem nomear, ela atribui esse valor ao primeiro valor, que é x, e soma com y default. Se eu passar os dois argumentos, a função esquece os valores default e soma os valores que eu passar como argumento. E eu posso, por fim, inverter a ordem nomeando os argumentos, tal qual feito logo no início do post.
Eu também posso solicitar na função que os parâmetros sejam explicitamente nomeados, adicionando um asterisco como parâmetro, dessa forma:
>>> def soma_param_explic_nomeados(*, x=10, y=20):
"""
x e y são parâmetros nomeados
"""
print(f'x = {x}')
print(f'y = {y}')
return x + y
Traduzindo o que foi definido acima, temos uma função que recebe dois parâmetros explicitamente nomeados e que não aceita parâmetros posicionais. Ou seja, se eu passar algum argumento posicional, o console irá gerar um erro informando que minha função não recebe argumentos posicionais.
>>> soma_param_explic_nomeados(5)
Traceback (most recent call last):
File "<pyshell#71>", line 1, in <module>
soma_param_explic_nomeados(5)
TypeError: soma_param_explic_nomeados() takes 0 positional arguments but 1 was given
>>> soma_param_explic_nomeados(5, 6)
Traceback (most recent call last):
File "<pyshell#72>", line 1, in <module>
soma_param_explic_nomeados(5, 6)
TypeError: soma_param_explic_nomeados() takes 0 positional arguments but 2 were given
>>> soma_param_explic_nomeados(y=33, x=22)
x = 22
y = 33
55
É importante aprender a ler os erros que são gerados. O console nos gerou TypeError, e explicou que a função não tem nenhum argumento posicional mas 1 foi dado na primeira chamada e 2 foram dados na segunda chamada. Ao nomear meus argumentos, o resultado é o esperado.
Interessante que eu posso definir uma função com parâmetros posicionais e explicitamente nomeados. O que vem antes do asterisco é posicional e o que vem depois é explicitamente nomeado:
>>> def soma_param_posic_e_explic_nomeados(a, b, *, x=10, y=20):
print(f'a={a}, b={b}, x={x}, y={y}')
return a + b + x + y
>>> soma_param_posic_e_explic_nomeados(5, 6)
a=5, b=6, x=10, y=20
41
>>> soma_param_posic_e_explic_nomeados()
Traceback (most recent call last):
File "<pyshell#79>", line 1, in <module>
soma_param_posic_e_explic_nomeados()
TypeError: soma_param_posic_e_explic_nomeados() missing 2 required positional arguments: 'a' and 'b'
>>> soma_param_posic_e_explic_nomeados(x=5, y=6, a=1, b=2)
a=1, b=2, x=5, y=6
14
Dessa forma podemos gerar funções que recebem argumentos que são posicionais e argumentos que necessitamos que sejam nomeados, ou explicitamente nomeados.
Por fim, podemos combinar tudo isso com *args e **kwargs, mas nesse caso não podemos ter argumentos explicitamente nomeados, pois o interpretador irá gerar um erro ao detectar dois asteriscos dentro da passagem de parâmetros:
>>> def soma_tudo(a, b, *, x=10, y=20, *args, **kwargs):
SyntaxError: invalid syntax
>>> def printa_tudo(a, b, x=10, y=20, *args, **kwargs):
print(f'a={a}, b={b}, x={x}, y={y}, args={args}, kwargs={kwargs}')
>>> printa_tudo(5, 15, 55, 65, *[i for i in range(10, 16)], **{'nome': 'José', 'endereço': 'Rua das Oliveiras 555'})
a=5, b=15, x=55, y=65, args=(10, 11, 12, 13, 14, 15), kwargs={'nome': 'José', 'endereço': 'Rua das Oliveiras 555'}