Funções

Contratos, retorno, argumentos, âmbito léxico, efeitos e decomposição de problemas.

TLDR

Perguntar sobre esta página

ChatGPTClaudePerplexityGrokGeminiCopiar e abrirDeepSeekCopiar e abrir

Para decidir se 6 é um número perfeito, somamos os seus divisores positivos menores que 6: 1+2+3=61+2+3=6. Podemos separar essa soma da comparação com o número original. Uma função dá um nome a cada cálculo e permite chamá-lo com argumentos diferentes, sem repetir o código.

Definir e chamar

def define uma função. Os nomes na definição são parâmetros; os valores de uma chamada são argumentos. Os dois pontos e a indentação delimitam o corpo, como num if. Segue quadrado(5): a chamada associa x a 5, calcula 5 * 5 e return entrega 25 ao chamador.

def quadrado(x):
    return x * x

print(quadrado(5))
print(quadrado(2) + quadrado(3))

A saída é 25 e 13. Definir a função não executa o seu corpo. Cada chamada tem os seus nomes locais. O resultado de uma chamada pode participar noutra expressão.

Return e print

return termina a chamada e entrega um valor ao chamador. print escreve na saída e devolve None. Uma função que chega ao fim sem return, ou executa return sem expressão, também devolve None.

def saudar(nome):
    print("Olá,", nome)

resposta = saudar("Ana")
print(resposta)

A saída é Olá, Ana e depois None. A saudação é um efeito da chamada, não o seu resultado. Se pretendes reutilizar o texto, escreve return "Olá, " + nome e imprime apenas fora da função. Num exercício que pede uma função, confirma se deve devolver ou imprimir.

Contrato e decomposição

O contrato indica o que a função recebe, as condições exigidas e o resultado. soma_divisores recebe um inteiro positivo e devolve a soma dos seus divisores próprios. e_perfeito recebe qualquer inteiro e devolve False para os não positivos. Uma docstring descreve o contrato entre aspas triplas no início do corpo; não valida a entrada.

Reconhecer números perfeitos
def soma_divisores(n):
    """Soma os divisores próprios do inteiro positivo n."""
    soma = 0
    for d in range(1, n):
        if n % d == 0:
            soma += d
    return soma

def e_perfeito(n):
    return n > 0 and soma_divisores(n) == n

print(e_perfeito(6), e_perfeito(12), e_perfeito(28))
print(e_perfeito(1), e_perfeito(0))
Resultado

Executa o código para ver a saída e os gráficos.

Para 6, range(1, 6) visita 1, 2, 3, 4 e 5; o teste 6 % d == 0 aceita os três primeiros e rejeita 4 e 5, pelo que o acumulador termina em 6 e a função exterior confirma a igualdade com a entrada.

Os divisores próprios de 12 somam 1+2+3+4+6=161+2+3+4+6=16, por isso 12 não é perfeito, enquanto para 28 a soma 1+2+4+7+14=281+2+4+7+14=28 confirma que é. O 1 tem soma vazia de divisores próprios, igual a zero, por isso também não é perfeito.

A função exterior decide e a interior resolve a soma; n > 0 and ... evita chamar a função interior fora da sua condição de entrada. O exemplo testa números perfeitos, um número que não é perfeito, a fronteira 1 e a entrada zero.

A mesma procura de divisores serve para outra decisão: um número primo é um inteiro maior que 1 cujos únicos divisores positivos são 1 e o próprio número.

Para decidir se n é primo, procura um divisor de 2 até n - 1. Encontrar um basta para devolver False; só devolve True depois de excluir todos. O 2 é primo, embora esse percurso seja vazio; o 1 não é primo.

Âmbito das variáveis

O âmbito diz onde um nome pode ser usado. Um nome atribuído dentro de uma função é normalmente local a essa função. A função procura nomes no âmbito local, nos âmbitos envolventes, no módulo e nos nomes predefinidos. Esta procura segue a estrutura do código, o âmbito léxico, não as variáveis locais de quem chamou.

valor = 10

def dobrar(valor):
    return 2 * valor

print(dobrar(4), valor)

A saída é 8 10 porque o parâmetro local não altera a associação global. Se atribuíres a valor no corpo, Python trata-o como local nesse corpo. Ler esse local antes de o definir causa UnboundLocalError.

global permite reatribuir um nome do módulo, mas cria uma dependência adicional; prefere parâmetros e retorno para um cálculo.

Uma função pode conservar acesso a nomes de uma função envolvente mesmo depois de essa chamada acabar; esse comportamento, uma closure, está explicado em programação funcional. Por isso, não confundas âmbito com a afirmação de que todos os objetos locais são destruídos no fim da chamada.

Listas, o mínimo necessário

[12, 15] é uma lista com dois números; [] é a lista vazia.

xs.append(9) acrescenta 9 à própria lista e devolve None. Já xs + [9] constrói outra lista, deixando xs intacta. Esta diferença permite observar os efeitos de uma função sobre os dados recebidos.

A lição de tuplos e listas desenvolve as restantes operações e a cópia de listas.

Passagem de argumentos e efeitos

Python associa o parâmetro ao objeto passado, sem o copiar. Reatribuir o parâmetro muda só o nome local:

def tentar_mudar(x):
    x = 99

n = 5
tentar_mudar(n)
print(n)

A saída é 5. A chamada associa x a 5 e depois associa x a 99; o nome n continua associado a 5.

Alterar um objeto recebido é diferente de reatribuir o parâmetro. Uma lista permite observar essa diferença: valores.append(99) altera a lista partilhada, enquanto return valores + [99] devolve outra lista. A lição de listas segue as referências num esquema.

Argumentos por nome e valores por defeito

Em def potencia(base, expoente=2):, podes chamar potencia(3) ou potencia(base=3, expoente=4). Um argumento por nome identifica o parâmetro. Os parâmetros obrigatórios anteriores não deixam de ser necessários.

Os valores por defeito são calculados quando def executa, não em cada chamada. Uma lista usada como valor por defeito fica partilhada entre chamadas: cada append altera o mesmo objeto guardado na definição. A lição de listas mostra essa partilha num esquema. Para criar uma lista nova quando não é passada nenhuma, usa None como sinal e cria-a no corpo.

def juntar(valor, valores=None):
    if valores is None:
        valores = []
    return valores + [valor]

print(juntar(1), juntar(2))

A saída é [1] [2]. O is None testa o objeto especial None, que é o uso apropriado de identidade neste caso. O contrato desta função também conserva qualquer lista recebida.

Exercícios

Seguir um parâmetro

Há x = 10 no módulo. Uma função f(x) executa x = x + 1 e devolve x. Depois de f(3), quanto vale o x do módulo?

A tua resposta

Pista
O parâmetro é local à chamada.
Mais uma pista
Reatribuir esse parâmetro não reatribui o nome do módulo.
Solução
O x do módulo continua a valer 10; a chamada devolve 4.
  • 4. 4 é o resultado da chamada, não o valor global.
  • 11. A função recebeu 3, e não usou o x do módulo.
  • 10. A atribuição altera apenas o parâmetro local.

Erros frequentes

Confundir nomes iguais em âmbitos diferentes.
Voltar à explicação
Reatribuir ou alterar

Com xs = [2, 4], uma função executa xs.append(6); outra executa xs = [8]. Explica o efeito de chamar cada uma sobre a lista original.

Pista
Os parâmetros começam associados à mesma lista recebida.
Mais uma pista

append altera o objeto; uma atribuição muda só o nome local.

Solução

A chamada com append deixa a lista original como [2, 4, 6]. A função que reatribui o parâmetro não substitui a lista no chamador. Se a segunda chamada vier depois da primeira, a lista permanece [2, 4, 6].

Confere a tua resposta:

  • Distingui alteração do objeto de reatribuição local.
  • Previ o efeito visível no chamador.
  • Expliquei que não há cópia automática.

Erros frequentes

Dizer que passar argumentos copia automaticamente os objetos.
Voltar à explicação
Encontrar estado partilhado

Uma função juntar(x, xs=[]) acrescenta x com append e devolve xs. As chamadas juntar(1) e depois juntar(2) usam listas diferentes? Explica e propõe uma correção.

Pista

A lista por defeito é criada quando def executa.

Mais uma pista
As chamadas que omitem xs reutilizam esse mesmo objeto.
Solução

Usam a mesma lista. A primeira chamada deixa [1]; a segunda acrescenta 2 e devolve [1, 2]. Usa xs=None e cria uma lista no corpo quando xs is None. Para preservar também listas recebidas, devolve xs + [x].

Confere a tua resposta:

  • Identifiquei a partilha da lista por defeito.
  • Previ [1, 2] na segunda chamada.
  • Propus None e criação no corpo.

Erros frequentes

Afirmar que [] é avaliado de novo em cada chamada.

Voltar à explicação
Escrever um contrato

Define o contrato de uma função media que recebe uma sequência de números e devolve a média. Que decisão falta para a entrada vazia?

Pista
A média divide a soma pelo número de valores.
Mais uma pista
O número de valores da sequência vazia é zero.
Solução

O contrato deve exigir uma sequência não vazia, ou especificar que a entrada vazia levanta uma exceção. Deve dizer que devolve sum(valores) / len(valores) sem alterar os argumentos. Zero não é uma média válida por convenção sem que isso seja definido.

Confere a tua resposta:

  • Defini o domínio numérico.
  • Tratei a sequência vazia explicitamente.
  • Indiquei o resultado e a preservação da entrada.

Erros frequentes

Devolver zero para esconder uma divisão impossível.
Voltar à explicação
Devolver uma decisão

Define e_primo(n), que recebe um inteiro e devolve True exatamente quando n é primo. Inteiros menores que 2 não são primos. Não leias dados nem imprimas no corpo da função.

Python
def e_primo(n):
    return False
Testes e resultado

Verifica o código para comparar o resultado com os testes.

Testes
  1. Não positivos
    Código do teste
    print(e_primo(-3), e_primo(0))
    Esperado:
    False False
  2. Fronteira
    Código do teste
    print(e_primo(1), e_primo(2))
    Esperado:
    False True
  3. Ímpar primo
    Código do teste
    print(e_primo(17))
    Esperado:
    True
  4. Compostos pares e ímpares
    Código do teste
    print(e_primo(4), e_primo(9), e_primo(25))
    Esperado:
    False False False

Pista

Trata primeiro os inteiros que ficam fora da definição de primo.

Mais uma pista

Para n >= 2, procura um divisor de 2 até n - 1. Se encontrares um, já podes devolver False.

Solução

Um divisor encontrado basta para rejeitar o número. O True fica depois do ciclo, quando todos os divisores foram excluídos. Para 2, o ciclo vazio chega corretamente a True.

def e_primo(n):
    if n < 2:
        return False
    for divisor in range(2, n):
        if n % divisor == 0:
            return False
    return True

Erros frequentes

Devolver True na primeira iteração sem divisor, testar só a paridade ou considerar 1 primo.

Voltar à explicação
Devolver em vez de imprimir

Define saudar(nome): recebe uma string e devolve exatamente “Olá, ” seguida do nome, sem imprimir. Por exemplo, saudar(“Ana”) deve devolver “Olá, Ana”, sem exclamação.

Python
def saudar(nome):
    return ""
Testes e resultado

Verifica o código para comparar o resultado com os testes.

Testes
  1. Devolve texto
    Código do teste
    print(saudar("Ana"))
    Esperado:
    Olá, Ana
  2. Outro nome
    Código do teste
    print(saudar("Bruno"))
    Esperado:
    Olá, Bruno
  3. Reutiliza
    Código do teste
    print(saudar("Ana") + "!")
    Esperado:
    Olá, Ana!

Pista

print escreve; return entrega o valor ao chamador.

Mais uma pista

O teste saudar("Ana") + "!" só passa se a função devolver texto: com print dentro e sem return, o resultado é None e a concatenação falha com TypeError.

Solução

Devolve "Olá, " + nome e imprime apenas fora da função. print devolve None; uma função que só imprime e chega ao fim sem return também devolve None.

def saudar(nome):
    return "Olá, " + nome

Erros frequentes

Usar print em vez de return e devolver None; incluir a exclamação dentro da função quando o contrato pede só a saudação.

Voltar à explicação

À tua maneira

Aparência

Modo de cor
Tema
Cor de destaque

Leitura

Fonte de leitura

Álgebra, lógica e uma ideia de cada vez.

Código

Fonte do código

Navegação

Cursor de leitura, sem edição. h, j, k, l movem; / pesquisa nesta página. v seleciona caracteres; V seleciona linhas; y copia. Tab permite destacar ou comentar. Espaço ou Enter abre blocos recolhíveis. Nos cartões, h, j, k, l mudam o foco.

CSS personalizado

Ativa uma sugestão ou guarda o teu CSS. Só muda este navegador.

Usa --page, --text e --accent para acompanhar o tema.

Seletores e recuperação

.site-header, .prose, .course-card, [data-reading-history], [data-playground] e .page-actions.

Abre a aparência com a tecla vírgula ou no rodapé. Se o CSS esconder os controlos, abre esta página sem CSS personalizado.

Pesquisar

Escreve para pesquisar em todo o site.

para escolher · Enter para abrir · Esc para fechar

Copiar fórmula

A cópia automática não está disponível. Copia o LaTeX abaixo.

Pesquisar nesta página

Enter procura · n / N repetem · Esc cancela. Ctrl / Cmd K pesquisa todo o site.

Atalhos de teclado

Clica numa tecla para a mudar. Esc cancela. Backspace desativa.

PesquisarCtrl / Cmd K

Os atalhos não interferem enquanto escreves. Tab e Enter funcionam sempre.