12. Funções: def, parâmetro e o que ela devolve¶
Nesta aula
Ideia central: o semestre inteiro você chamou funções prontas —
print, input, len, round, range — sem precisar saber o nome
disso. Esta aula ensina a escrever as suas: um pedaço de programa com
nome, que recebe valores e, na maioria das vezes, devolve uma
resposta para quem chamou usar.
Ao fim você vai conseguir: definir uma função com def; distinguir
parâmetro (na definição) de argumento (na chamada); usar
return para devolver um valor; explicar por que uma função que só
imprime é mais limitada que uma que devolve; e reconhecer o defeito
central desta unidade — uma função que "funciona" na tela e devolve
None.
Você já chama funções há treze capítulos¶
print("oi"), input(), len(lista), round(3.14159, 2) — todos eles têm a
mesma forma: um nome, seguido de parênteses, com valores entre eles. Isso é
chamar uma função. Você só nunca precisou definir uma, porque o Python já
trouxe essas prontas.
A partir de hoje você escreve as suas.
Definir não é executar¶
O bloco def saudacao(nome): não imprime nada sozinho — ele só ensina ao
Python o que fazer quando alguém chamar saudacao(...). Nada acontece até
a primeira linha fora da função, saudacao("Ana"), rodar de verdade. É a
mesma diferença de uma receita e o prato pronto: escrever a receita não
alimenta ninguém.
💡 Pense assim: a receita e o prato
def escreve a receita: o passo a passo, guardado com um nome. Cada
chamada — saudacao("Ana"), saudacao("Bruno") — é fazer o prato uma
vez, com os ingredientes daquela vez. A mesma receita, prato depois de
prato, com ingredientes diferentes.
Parâmetro × argumento¶
def situacao_franquia(consumo_gb, franquia_gb):
print(f"Consumo: {consumo_gb} GB | Franquia: {franquia_gb} GB")
if consumo_gb > franquia_gb:
print("Passou da franquia")
else:
print("Dentro da franquia")
situacao_franquia(12, 10)
situacao_franquia(7, 10)
Consumo: 12 GB | Franquia: 10 GB
Passou da franquia
Consumo: 7 GB | Franquia: 10 GB
Dentro da franquia
Dois nomes para duas coisas diferentes, e a disciplina seguinte já vai supor que você os distingue:
| Termo | Onde aparece | No exemplo |
|---|---|---|
| parâmetro | na linha do def, é um nome que ainda não tem valor |
consumo_gb, franquia_gb |
| argumento | na chamada, é o valor de verdade que você está passando | 12 e 10, depois 7 e 10 |
A função foi definida uma vez. Cada chamada manda argumentos diferentes, e o corpo dela roda de novo, do zero, com esses valores nos parâmetros. Repare que a segunda chamada não "lembra" nada da primeira.
return: devolver um valor para quem chamou¶
Tudo que você viu até aqui só imprime — mostra alguma coisa na tela, para
uma pessoa ler. Mas um programa quase sempre precisa usar o resultado de
uma conta em outra conta, e é aí que print não basta.
def calcula_excedente(consumo_gb, franquia_gb):
excedente = consumo_gb - franquia_gb
print(excedente)
calcula_excedente(12, 10)
Essa função calcula o excedente e mostra 2 na tela. Funciona — até você
precisar do excedente para calcular quanto cobrar por ele. O 2 que apareceu
não está guardado em lugar nenhum; ele foi escrito na tela e sumiu. Para
usá-lo de novo você teria que copiar a conta inteira.
Compare com a mesma função usando return:
def calcula_excedente(consumo_gb, franquia_gb):
excedente = consumo_gb - franquia_gb
return excedente
excedente = calcula_excedente(12, 10)
custo = excedente * 5
print(f"Excedente: {excedente} GB")
print(f"Cobranca extra: R$ {custo}")
return excedente entrega o valor calculado para quem chamou a função — e
excedente = calcula_excedente(12, 10) guarda essa entrega numa variável, do
mesmo jeito que input() guarda o que a pessoa digitou. Com o valor em mãos,
o programa multiplica por 5 para achar o custo, sem repetir a conta do
excedente em lugar nenhum. É por isso que devolver é quase sempre melhor que
imprimir: o resultado de uma função com return pode ser guardado, somado,
comparado, formatado — usado de novo em qualquer lugar do programa. O
resultado de uma função que só imprime não pode ser usado em lugar nenhum:
ele já foi para a tela, e a tela não devolve nada ao programa.
💡 Pense assim: o caixa eletrônico e o extrato pregado na parede
Uma função com return é o caixa eletrônico: ele entrega o dinheiro na
sua mão, e você decide o que fazer com ele — guardar, gastar, contar de
novo. Uma função que só imprime é o extrato pregado na parede do banco:
você lê o número, mas não sai dali com nada que o programa consiga
seguir usando.
A armadilha: a função que imprime o número certo e devolve None¶
Este é o defeito central da unidade, e ele é traiçoeiro porque parece funcionar.
def dobro(numero):
print(numero * 2) # imprime, mas nao devolve nada
resultado = dobro(5)
print(resultado)
dobro(5) imprime 10 — o número certo, na tela, na hora certa. Quem está
olhando jura que a função funcionou. Mas resultado = dobro(5) não guarda o
10 em lugar nenhum, porque dobro nunca usou return. Sem return,
toda função Python devolve None por padrão — um valor que significa "nada
aqui". resultado vale None, e print(resultado) mostra exatamente isso.
print mostra; return entrega. Uma função sem return sempre devolve None
Não importa quantos print existam dentro dela: se não houver um
return, o valor que a função devolve é None. O programa não trava, não
avisa nada — o 10 na tela é real, e ainda assim resultado está vazio.
Só quebra na hora em que alguém tenta usar resultado numa conta, e aí a
causa já está longe, lá na função que "funcionava".
| Função que imprime | Função que devolve | |
|---|---|---|
| O que faz | mostra um valor na tela | entrega um valor para quem chamou |
| Termina em | print(...) |
return ... |
| Dá para guardar o resultado numa variável? | não — vem None |
sim |
| Dá para usar o resultado em outra conta? | não | sim |
Dá para testar com assert? |
não, direto | sim — é o que a Lista 12 faz |
Uma função pode ter vários parâmetros — e geralmente um só retorno¶
situacao_franquia(consumo_gb, franquia_gb), lá em cima, já tinha dois
parâmetros: a ordem em que você os escreve no def é a mesma ordem em que os
argumentos entram na chamada. Uma função pode receber quantos parâmetros
precisar, mas quase sempre devolve um valor por chamada — se ela precisa
calcular duas coisas diferentes, o mais comum é escrever duas funções, uma
para cada uma.
Erros comuns deste capítulo¶
| Mensagem ou sintoma | Causa provável |
|---|---|
TypeError: nome_da_funcao() missing 1 required positional argument: 'x' |
chamou a função com menos argumentos do que ela tem parâmetros |
TypeError: nome_da_funcao() takes 2 positional arguments but 3 were given |
chamou com argumentos a mais |
SyntaxError: expected ':' |
faltaram os dois-pontos no fim da linha do def |
IndentationError: expected an indented block |
o corpo da função não foi recuado |
a tela mostrou o número certo, mas a variável guardada é None |
a função usa print onde devia usar return |
<function nome_da_funcao at 0x...> no lugar do resultado esperado |
esqueceu os parênteses na chamada — escreveu nome_da_funcao, não nome_da_funcao() |
Resumo¶
def nome(parametros):define a função; nada dela roda até alguém chamarnome(argumentos).- Parâmetro é o nome na definição; argumento é o valor na chamada.
return valordevolve o valor para quem chamou, e permite guardá-lo numa variável e reutilizá-lo.printsó mostra na tela, para uma pessoa ler.- O erro que não dá erro desta unidade: uma função sem
return"funciona" na tela — mostra o número certo — e devolveNonepara o programa. Só quebra quando alguém tenta usar o resultado. - Uma função pode ter vários parâmetros, mas normalmente devolve um valor só.
Para praticar¶
Todo exercício desta lista é, pela primeira vez, uma função com testes prontos no fim do arquivo — o mesmo formato de Python para Telecomunicações. Antes de entregar qualquer um deles, pergunte-se: minha função devolve o valor, ou só mostra na tela?