5  Funções

Até aqui, nossos programas seguiam um fluxo linear: o Python executava cada instrução na ordem em que ela aparecia. Essa abordagem funciona bem para tarefas simples, mas rapidamente se torna problemática à medida que os programas crescem.

Imagine que você precisa calcular o PIB pela ótica da despesa em vinte cenários diferentes. Sem funções, você repetiria a mesma fórmula vinte vezes — e bastaria um erro em uma das cópias para comprometer toda a análise. Funções resolvem esse problema: elas permitem que um bloco de código seja escrito uma única vez e reutilizado quantas vezes for necessário, com diferentes valores de entrada.

💡 Analogia econômica: pense em uma função como uma máquina de produção. Você fornece insumos (parâmetros), a máquina executa um processo (instruções) e entrega um produto (retorno). A mesma máquina pode ser acionada várias vezes com insumos diferentes, sempre produzindo o resultado correto.

5.1 Definindo uma função

Para definir uma função em Python, usamos a palavra-chave def, seguida do nome da função e dos parâmetros entre parênteses. Todo o bloco de código indentado abaixo do cabeçalho fará parte da função.

def nome_da_funcao(parametros):
    <instruções>
    ...
    return <valor>  # opcional

Os elementos da sintaxe são:

  • def: indica ao Python que uma função está sendo definida.
  • nome_da_funcao: nome descritivo que indica o que a função faz.
  • parametros: valores que a função recebe para processar (opcional).
  • return: devolve um resultado para quem chamou a função (opcional).

O exemplo mais simples é uma função sem parâmetros:

def saudacao():
    print("Olá, seja bem-vindo(a)!")


saudacao()
Olá, seja bem-vindo(a)!

5.2 Parâmetros

Os parâmetros tornam as funções flexíveis: permitem que diferentes valores sejam processados sem alterar o código da função. O mesmo bloco pode ser reutilizado em várias partes do programa com diferentes entradas.

def saudacao(nome):
    print(f"Olá, {nome}! Seja bem-vindo(a)!")


saudacao("Arthur")
saudacao("Mariana")
Olá, Arthur! Seja bem-vindo(a)!
Olá, Mariana! Seja bem-vindo(a)!

5.2.1 Valores padrão

Podemos atribuir um valor padrão a um parâmetro. Se nenhum argumento for passado, o Python usa o padrão automaticamente:

def saudacao(nome="visitante"):
    print(f"Olá, {nome}! Seja bem-vindo(a)!")


saudacao("Arthur")
saudacao()           # usa o padrão: "visitante"
Olá, Arthur! Seja bem-vindo(a)!
Olá, visitante! Seja bem-vindo(a)!

5.2.2 Parâmetros posicionais e nomeados

Quando uma função recebe vários parâmetros, o Python os associa pela ordem em que são passados — isso é chamado de argumento posicional:

def apresentar(nome, curso):
    print(f"Meu nome é {nome} e o meu curso é {curso}")


apresentar("Arthur", "Economia")
Meu nome é Arthur e o meu curso é Economia

Se a ordem for trocada acidentalmente, o resultado fica semanticamente incorreto:

apresentar("Economia", "Arthur")  # saída incorreta!
Meu nome é Economia e o meu curso é Arthur

Para evitar esse tipo de erro, podemos usar argumentos nomeados, informando explicitamente a qual parâmetro cada valor pertence. Com argumentos nomeados, a ordem não importa:

apresentar(nome="Arthur", curso="Economia")
apresentar(curso="Economia", nome="Arthur")  # mesmo resultado
Meu nome é Arthur e o meu curso é Economia
Meu nome é Arthur e o meu curso é Economia

5.2.3 Exemplo: juros compostos

A fórmula de juros compostos \(M = C \cdot (1 + i)^t\) é um conceito central em finanças. O parâmetro padrão periodos=1 é útil quando queremos calcular o rendimento de um único período sem precisar especificá-lo toda vez:

def juros_compostos(capital, taxa, periodos=1):
    montante = capital * (1 + taxa) ** periodos
    rendimento = montante - capital
    print(f"Capital inicial:  R$ {capital:,.2f}")
    print(f"Taxa por período: {taxa*100:.1f}%")
    print(f"Períodos:         {periodos}")
    print(f"Rendimento:       R$ {rendimento:,.2f}")
    print(f"Montante final:   R$ {montante:,.2f}")


print("--- Rendimento da poupança em 1 mês ---")
juros_compostos(capital=10_000, taxa=0.005)        # usa o padrão periodos=1

print()
print("--- Rendimento do Tesouro Selic em 12 meses ---")
juros_compostos(capital=10_000, taxa=0.005, periodos=12)
--- Rendimento da poupança em 1 mês ---
Capital inicial:  R$ 10,000.00
Taxa por período: 0.5%
Períodos:         1
Rendimento:       R$ 50.00
Montante final:   R$ 10,050.00

--- Rendimento do Tesouro Selic em 12 meses ---
Capital inicial:  R$ 10,000.00
Taxa por período: 0.5%
Períodos:         12
Rendimento:       R$ 616.78
Montante final:   R$ 10,616.78

5.3 Retornando valores

Até agora, nossas funções apenas exibiam resultados com print(). A palavra-chave return permite que uma função devolva um resultado para quem a chamou, possibilitando seu uso em expressões e cálculos posteriores.

Essa é uma das distinções mais importantes para iniciantes:

# Versão com print: o resultado é exibido, mas não pode ser reutilizado
def dobro_print(n):
    print(n * 2)

# Versão com return: o resultado é devolvido e pode ser usado em cálculos
def dobro_return(n):
    return n * 2


resultado_print = dobro_print(5)    # exibe 10...
print(resultado_print)              # ...mas a variável guarda None!

print()

resultado_return = dobro_return(5)
print(resultado_return)             # 10
print(resultado_return + 3)         # 13 — podemos continuar operando!
10
None

10
13
Dicaprint() vs. return

Use print() quando quiser apenas mostrar algo ao usuário. Use return quando quiser que a função produza um valor que será usado em outro lugar do programa. Para construir modelos econômicos com vários cálculos encadeados, return é indispensável.

5.3.1 Exemplo: encadeando cálculos

A modularização — dividir um problema em funções menores, cada uma fazendo uma coisa só — é uma das práticas mais importantes em programação. O exemplo abaixo calcula o salário líquido descontando INSS e IRPF, com cada etapa em sua própria função:

def calcular_inss(salario_bruto):
    teto_inss = 8_475.55  # teto do INSS em 2026
    salario_inss = min(salario_bruto, teto_inss)

    faixas_inss = [
        (1_621.00, 0.075),
        (2_902.84, 0.09),
        (4_354.27, 0.12),
        (teto_inss, 0.14)
    ]

    inss = 0
    anterior = 0
    for limite, aliquota in faixas_inss:
        if salario_inss > limite:
            inss += (limite - anterior) * aliquota
            anterior = limite
        else:
            inss += (salario_inss - anterior) * aliquota
            break
    return inss

def calcular_irpf(base_irpf):
    # Tabela mensal do IRPF vigente a partir de maio de 2025
    faixas_irpf = [
        (2_428.80, 0.00, 0.00),
        (2_826.65, 0.075, 182.16),
        (3_751.05, 0.15, 394.16),
        (4_664.68, 0.225, 675.49),
        (float("inf"), 0.275, 908.73),
    ]

    irpf = 0
    for limite, aliquota, deducao in faixas_irpf:
        if base_irpf <= limite:
            irpf = (base_irpf * aliquota) - deducao
            break

    return max(irpf, 0)  # IR não pode ser negativo

def salario_liquido(salario_bruto):
    desconto_inss = calcular_inss(salario_bruto)
    base_irpf = salario_bruto - desconto_inss
    desconto_irpf = calcular_irpf(base_irpf)
    liquido = salario_bruto - desconto_inss - desconto_irpf
    return liquido

# Testando para diferentes faixas salariais
salarios = [1_500, 3_000, 6_000, 12_000]

print(f"{'Salário Bruto':>16} | {'INSS':>10} | {'IRPF':>10} | {'Salário Líquido':>16}")
print("-" * 62)

for bruto in salarios:
    inss = calcular_inss(bruto)
    irpf = calcular_irpf(bruto - inss)
    liquido = salario_liquido(bruto)
    print(f"R$ {bruto:>12,.2f} | R$ {inss:>7,.2f} | R$ {irpf:>7,.2f} | R$ {liquido:>13,.2f}")
   Salário Bruto |       INSS |       IRPF |  Salário Líquido
--------------------------------------------------------------
R$     1,500.00 | R$  112.50 | R$    0.00 | R$      1,387.50
R$     3,000.00 | R$  248.60 | R$   24.20 | R$      2,727.21
R$     6,000.00 | R$  641.51 | R$  564.85 | R$      4,793.63
R$    12,000.00 | R$  988.09 | R$ 2,119.54 | R$      8,892.36

5.3.2 Múltiplos retornos

Uma função pode retornar mais de um valor separando-os por vírgula. O Python os agrupa em uma tupla, que pode ser desempacotada em variáveis distintas:

def operacoes(a, b):
    soma    = a + b
    produto = a * b
    return soma, produto


s, p = operacoes(2, 3)
print(s, p)  # Saída: 5 6
5 6

Também é possível retornar dicionários, o que é especialmente útil quando os resultados têm nomes significativos:

import statistics

def descritivas(x):
    media        = statistics.mean(x)
    desvio_padrao = statistics.stdev(x)
    return {"media": media, "desvio_padrao": desvio_padrao}


dados = [1, 2, 3, 4, 5]
print(descritivas(dados))
{'media': 3, 'desvio_padrao': 1.5811388300841898}

5.3.3 Exemplo: métricas de inflação

O exemplo abaixo calcula a inflação acumulada e a média mensal a partir de uma lista de taxas mensais, retornando ambas em uma única chamada:

def analisar_inflacao(taxas_mensais):
    # Inflação acumulada: produto de (1 + taxa) para cada mês
    acumulada = 1
    for taxa in taxas_mensais:
        acumulada *= (1 + taxa)
    acumulada -= 1  # converte de volta para formato decimal

    media_mensal = statistics.mean(taxas_mensais)
    return acumulada, media_mensal


# IPCA mensal hipotético (em decimal)
ipca_2024 = [0.0042, 0.0083, 0.0016, 0.0038, 0.0044,
             0.0050, 0.0038, 0.0044, 0.0044, 0.0056,
             0.0039, 0.0052]

acum, media = analisar_inflacao(ipca_2024)

print(f"Inflação acumulada no ano: {acum*100:.2f}%")
print(f"Inflação média mensal:     {media*100:.2f}%")
Inflação acumulada no ano: 5.60%
Inflação média mensal:     0.46%

5.4 Aplicação: Teorema Central do Limite

No capítulo anterior, escrevemos um código para simular o Teorema Central do Limite (TCL): ao retirar muitas amostras de uma distribuição e calcular a média de cada uma, a distribuição dessas médias tende a ser normal — independentemente da distribuição original.

O código original funcionava, mas tinha uma limitação: os parâmetros da simulação estavam fixados diretamente no código. Para testar cenários diferentes, era preciso reescrevê-lo toda vez.

Transformar esse código em uma função reutilizável resolve o problema. Os parâmetros com valores padrão (n_amostras=10000, tamanho_amostra=30, seed=1234) deixam a função flexível: ela funciona sem nenhum argumento, mas também aceita configurações personalizadas.

import numpy as np

def simular_tcl(n_amostras=10000, tamanho_amostra=30, seed=1234):
    """
    Simula o Teorema Central do Limite usando amostras de uma
    distribuição uniforme entre 0 e 1.

    Parâmetros:
        n_amostras (int): número de amostras a gerar. Padrão: 10000.
        tamanho_amostra (int): observações por amostra. Padrão: 30.
        seed (int): semente para reprodutibilidade. Padrão: 1234.

    Retorna:
        float: média das médias amostrais.
    """
    np.random.seed(seed)
    medias = []

    for _ in range(n_amostras):
        amostra = np.random.uniform(0, 1, tamanho_amostra)
        medias.append(np.mean(amostra))

    return np.mean(medias)


resultado = simular_tcl()
print(f"Média das médias (padrão): {resultado:.4f}")
Média das médias (padrão): 0.4994

5.4.1 Verificando a convergência

Uma das demonstrações clássicas do TCL é observar como a média das médias converge para o valor teórico (\(\mu = 0{,}5\) no caso da \(U(0,1)\)) conforme aumentamos o número de amostras. Com a função, isso fica muito mais claro:

lista_n_amostras = [1, 10, 100, 1_000, 10_000, 100_000]

print(f"{'Nº de amostras':>16} | {'Média das médias':>18} | {'Erro vs. 0.5':>14}")
print("-" * 56)

for n in lista_n_amostras:
    media = simular_tcl(n_amostras=n, tamanho_amostra=5)
    erro  = abs(media - 0.5)
    print(f"{n:>16,} | {media:>18.4f} | {erro:>14.4f}")
  Nº de amostras |   Média das médias |   Erro vs. 0.5
--------------------------------------------------------
               1 |             0.5633 |         0.0633
              10 |             0.5225 |         0.0225
             100 |             0.5205 |         0.0205
           1,000 |             0.4992 |         0.0008
          10,000 |             0.5002 |         0.0002
         100,000 |             0.4996 |         0.0004

Note que, ao aumentar o número de amostras, a média das médias se aproxima cada vez mais de 0,5 — exatamente o TCL em ação. E com a função, podemos explorar esse comportamento variando os parâmetros com apenas uma linha de código.

5.5 Docstrings

Docstrings são strings de documentação escritas logo abaixo do cabeçalho de uma função, entre aspas triplas ("""). Elas descrevem o que a função faz, quais parâmetros recebe e o que retorna.

Boas docstrings tornam o código muito mais fácil de entender e manter — especialmente quando você retorna a ele depois de semanas, ou quando compartilha com colegas. Você pode consultá-las a qualquer momento com help():

def taxa_desemprego(desempregados, populacao_ativa):
    """
    Calcula a taxa de desemprego de uma economia.

    Parâmetros:
        desempregados (float): número de pessoas desempregadas.
        populacao_ativa (float): tamanho da força de trabalho (PEA).

    Retorna:
        float: taxa de desemprego, expressa em percentual (%).
    """
    return (desempregados / populacao_ativa) * 100


help(taxa_desemprego)
Help on function taxa_desemprego in module __main__:

taxa_desemprego(desempregados, populacao_ativa)
    Calcula a taxa de desemprego de uma economia.
    
    Parâmetros:
        desempregados (float): número de pessoas desempregadas.
        populacao_ativa (float): tamanho da força de trabalho (PEA).
    
    Retorna:
        float: taxa de desemprego, expressa em percentual (%).
# Dados hipotéticos (em milhões de pessoas)
taxa = taxa_desemprego(desempregados=8.5, populacao_ativa=107.0)
print(f"Taxa de desemprego: {taxa:.1f}%")
Taxa de desemprego: 7.9%

5.6 Escopo de variáveis

O escopo de uma variável se refere ao local onde ela é definida e onde pode ser acessada. Python segue a regra LEGB para resolver os nomes: procura primeiro no escopo Local, depois no Enclosing, depois no Global e por fim no Built-in.

5.6.1 Escopo local

Uma variável definida dentro de uma função existe apenas dentro dela:

def funcao_local():
    mensagem = "Olá, Mundo!"  # variável local
    print(mensagem)


funcao_local()      # funciona
print(mensagem)     # NameError: não existe fora da função
Olá, Mundo!
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[17], line 7
      3     print(mensagem)
      6 funcao_local()      # funciona
----> 7 print(mensagem)     # NameError: não existe fora da função

NameError: name 'mensagem' is not defined

5.6.2 Escopo enclosing

Se uma função está dentro de outra, a interna pode acessar as variáveis da externa — mas não o contrário:

def funcao_externa():
    mensagem = "Olá, Mundo!"

    def funcao_interna():
        print(mensagem)   # acessa a variável da função externa

    funcao_interna()


funcao_externa()
Olá, Mundo!

5.6.3 Escopo global

Uma variável definida fora de todas as funções pode ser lida de qualquer lugar. No entanto, para modificá-la dentro de uma função, é necessário declará-la com a palavra-chave global.

Primeiro, a leitura sem problema:

poupanca = 1000

def banco():
    print(f"Saldo atual: R$ {poupanca}")   # lê a variável global


banco()
Saldo atual: R$ 1000

Agora, a tentativa de modificação sem global:

poupanca = 1000

def deposito(n):
    poupanca += n   # UnboundLocalError!

deposito(500)
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
Cell In[20], line 6
      3 def deposito(n):
      4     poupanca += n   # UnboundLocalError!
----> 6 deposito(500)

Cell In[20], line 4, in deposito(n)
      3 def deposito(n):
----> 4     poupanca += n

UnboundLocalError: local variable 'poupanca' referenced before assignment
NotaPor que o UnboundLocalError?

Ao encontrar poupanca += n, o Python interpreta poupanca como uma variável local da função — pois está sendo atribuída. Mas como ela ainda não tem valor local, o Python não sabe de onde buscá-la.

A solução é usar global para avisar ao Python que a variável em questão pertence ao escopo global:

poupanca = 1000

def deposito(n):
    global poupanca
    poupanca += n

def retirada(n):
    global poupanca
    poupanca -= n

def banco():
    deposito(500)
    retirada(200)
    print(f"Saldo atual: R$ {poupanca}")


banco()
Saldo atual: R$ 1300
AvisoBoa prática: evite global

Embora global resolva o problema, seu uso excessivo dificulta a leitura e a manutenção do código — fica difícil rastrear onde uma variável está sendo alterada. Em programas maiores, é preferível passar o valor como parâmetro e retorná-lo explicitamente. Você aprenderá formas mais robustas de gerenciar estado à medida que avançar no curso.

5.6.4 Escopo enclosing com nonlocal

A palavra-chave nonlocal funciona de forma similar a global, mas atua no escopo enclosing: permite que uma função interna modifique uma variável da função que a contém.

No exemplo abaixo, transacao precisa de nonlocal para incrementar o contador definido em registrar_transacoes:

def registrar_transacoes():
    total_transacoes = 0

    def transacao(descricao):
        nonlocal total_transacoes
        total_transacoes += 1
        print(f"  Transação {total_transacoes}: {descricao}")

    transacao("Depósito de R$ 500")
    transacao("Pagamento de boleto R$ 120")
    transacao("Transferência de R$ 200")
    print(f"Total de transações registradas: {total_transacoes}")


registrar_transacoes()
  Transação 1: Depósito de R$ 500
  Transação 2: Pagamento de boleto R$ 120
  Transação 3: Transferência de R$ 200
Total de transações registradas: 3

5.6.5 Escopo built-in

O escopo built-in contém os nomes pré-definidos do Python — funções como print, len, range e sum. Eles estão sempre disponíveis, em qualquer parte do código:

print(len("Python"))   # len é uma função built-in
6

5.7 args e kwargs

*args e **kwargs são formas flexíveis de passar múltiplos argumentos para uma função, permitindo lidar com quantidades variáveis de dados — algo bastante útil em modelos econômicos, onde o número de variáveis pode mudar.

Observe, por exemplo, a assinatura da função print:

print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)
  • *objects aceita qualquer número de argumentos posicionais: print("a", "b", "c").
  • end='\n' é um argumento nomeado com valor padrão: print("texto", end="").
Dicaargs e kwargs são convenções, não palavras reservadas

O que importa são os asteriscos: * coleta múltiplos argumentos posicionais em uma tupla, e ** coleta múltiplos argumentos nomeados em um dicionário. Você poderia escrever *numeros ou **indicadores com o mesmo resultado. args e kwargs são apenas o padrão adotado pela comunidade Python.

5.7.1 *args: múltiplos argumentos posicionais

def adicao(*args):
    resultado = 0
    for argumento in args:
        resultado += argumento
    return resultado


print(adicao(1, 2))
print(adicao(1, 2, 3, 4))
print(adicao(1, 2, 3, 4, 5, 6))
3
10
21

5.7.1.1 Exemplo: PIB com composição variável

Uma das identidades macroeconômicas mais fundamentais é:

\[Y = C + I + G + (X - M)\]

onde \(C\) é o consumo das famílias, \(I\) o investimento, \(G\) os gastos do governo, \(X\) as exportações e \(M\) as importações. Em vez de repetir essa fórmula toda vez que precisarmos calculá-la, podemos encapsulá-la em uma função:

def calcular_pib(consumo, investimento, gastos_governo, exportacoes, importacoes):
    pib = consumo + investimento + gastos_governo + (exportacoes - importacoes)
    print(f"PIB calculado: R$ {pib:.2f} bilhões")


# Dados hipotéticos (em bilhões de R$)
calcular_pib(
    consumo=4_800,
    investimento=1_100,
    gastos_governo=2_200,
    exportacoes=1_600,
    importacoes=1_400
)
PIB calculado: R$ 8300.00 bilhões

Com *args, podemos criar uma função que soma todos os componentes do PIB, independentemente de quantos forem passados:

def calcular_pib(*componentes):
    """Soma todos os componentes do PIB passados como argumentos."""
    return sum(componentes)


pib_simples  = calcular_pib(4800, 2200, 1600)              # C + I + G
pib_completo = calcular_pib(4800, 1100, 2200, 1600, -1400) # C+ I + G + (X - M)

print(f"PIB simplificado: R$ {pib_simples:,.0f} bi")
print(f"PIB completo:     R$ {pib_completo:,.0f} bi")
PIB simplificado: R$ 8,600 bi
PIB completo:     R$ 8,300 bi

5.7.2 **kwargs: múltiplos argumentos nomeados

**kwargs agrupa os argumentos passados com nome em um dicionário dentro da função:

def concatenar(**kwargs):
    print(f"Valores recebidos: {kwargs}")
    resultado = ""
    for valor in kwargs.values():
        resultado += f"{valor} "
    return resultado


print(concatenar(curso="Economia"))
print(concatenar(curso="Economia", faculdade="FEA-USP"))
Valores recebidos: {'curso': 'Economia'}
Economia 
Valores recebidos: {'curso': 'Economia', 'faculdade': 'FEA-USP'}
Economia FEA-USP 

5.7.2.1 Exemplo: relatório de indicadores macroeconômicos

Com **kwargs, podemos gerar relatórios com qualquer conjunto de variáveis — útil quando comparamos países com disponibilidades de dados diferentes:

def relatorio_macro(pais, **indicadores):
    """
    Exibe um relatório de indicadores macroeconômicos.

    Parâmetros:
        pais (str): nome do país.
        **indicadores: pares nome=valor para qualquer indicador econômico.
    """
    print(f"\n=== Indicadores Macroeconômicos: {pais} ===")
    for indicador, valor in indicadores.items():
        nome_formatado = indicador.replace("_", " ").title()
        print(f"  {nome_formatado}: {valor}")


relatorio_macro(
    "Brasil",
    pib_crescimento="2.9%",
    inflacao_ipca="4.83%",
    taxa_desemprego="6.2%",
    taxa_selic="10.5%"
)

relatorio_macro(
    "Argentina",
    inflacao="211%",
    taxa_desemprego="7.7%"
)

=== Indicadores Macroeconômicos: Brasil ===
  Pib Crescimento: 2.9%
  Inflacao Ipca: 4.83%
  Taxa Desemprego: 6.2%
  Taxa Selic: 10.5%

=== Indicadores Macroeconômicos: Argentina ===
  Inflacao: 211%
  Taxa Desemprego: 7.7%

5.7.3 Combinando *args e **kwargs

def relatorio_economico(*args, **kwargs):
    print("Valores agregados:", sum(args))
    for chave, valor in kwargs.items():
        print(f"{chave}: {valor}")


relatorio_economico(100, 200, consumo=5_000, investimento=2_000)
Valores agregados: 300
consumo: 5000
investimento: 2000

5.8 Funções anônimas (lambda)

Funções lambda são funções anônimas — definidas sem nome, em uma única linha. São úteis para operações simples e temporárias, especialmente como argumento para outras funções. A sintaxe é:

lambda <argumentos> : <expressão>

Um exemplo simples:

soma = lambda x, y: x + y
print(soma(5, 3))
8

5.8.1 Função map()

map() aplica uma função a cada elemento de uma sequência. Com lambda, isso fica muito conciso:

numeros  = [1, 2, 3, 4, 5]
quadrados = list(map(lambda x: x ** 2, numeros))
print(quadrados)
[1, 4, 9, 16, 25]

5.8.1.1 Exemplo: corrigindo preços pela inflação

Em economia, deflacionar uma série de preços significa remover o efeito da inflação para tornar valores de diferentes períodos comparáveis em termos reais. A fórmula geral é:

\[P_{\text{real}} = \frac{P_{\text{nominal}}}{1 + \pi}\]

onde \(P_{\text{nominal}}\) é o preço observado no período e \(\pi\) é a taxa de inflação acumulada entre o período de referência e o período de base. O denominador \((1 + \pi)\) atua como um deflator: quanto maior a inflação, mais o preço nominal é “encolhido” para revelar seu valor real.

NotaValor nominal vs. valor real

Um valor nominal é expresso nos preços correntes do período em que foi medido. Um valor real é corrigido pela inflação e expressa o poder de compra em termos de um período de referência (base). A distinção é fundamental em macroeconomia: o PIB pode crescer em termos nominais simplesmente porque os preços subiram, sem que haja crescimento real da produção.

No código abaixo, aplicamos a fórmula a cada preço da lista usando map() com uma função lambda — um caso típico em que lambda é mais conciso do que definir uma função completa com def:

precos_nominais    = [10.00, 12.50, 9.80, 15.00, 11.30]
inflacao_acumulada = 0.15 # 15% de inflação acumulada no período

precos_reais = list(map(lambda p: p / (1 + inflacao_acumulada), precos_nominais))

print("Preço nominal -> Preço real (deflacionado)")
for nominal, real in zip(precos_nominais, precos_reais):
    print(f"  R$ {nominal:.2f} -> R$ {real:.2f}")
Preço nominal -> Preço real (deflacionado)
  R$ 10.00 -> R$ 8.70
  R$ 12.50 -> R$ 10.87
  R$ 9.80 -> R$ 8.52
  R$ 15.00 -> R$ 13.04
  R$ 11.30 -> R$ 9.83

5.8.2 Função filter()

filter() seleciona os elementos de uma sequência que satisfazem um critério booleano:

numeros      = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
numeros_pares = list(filter(lambda x: x % 2 == 0, numeros))
print(numeros_pares)
[2, 4, 6, 8, 10]

5.8.2.1 Exemplo: países com inflação acima da meta

paises_inflacao = [
    ("Brasil",    4.8),
    ("Chile",     3.2),
    ("Argentina", 211.0),
    ("México",    4.7),
    ("Colômbia",  9.3),
    ("Peru",      2.9),
    ("Uruguai",   5.1),
]

meta_inflacao = 4.5

acima_da_meta = list(filter(lambda x: x[1] > meta_inflacao, paises_inflacao))

print(f"Países com inflação acima de {meta_inflacao}%:")
for pais, inf in acima_da_meta:
    print(f"  {pais}: {inf}%")
Países com inflação acima de 4.5%:
  Brasil: 4.8%
  Argentina: 211.0%
  México: 4.7%
  Colômbia: 9.3%
  Uruguai: 5.1%

5.8.3 Função sorted()

sorted() ordena sequências. O parâmetro key aceita uma função que define o critério de ordenamento.

NotaO método .items()

Dicionários em Python armazenam pares chave: valor. O método .items() retorna esses pares como uma sequência de tuplas (chave, valor), o que permite iterá-los e ordená-los como qualquer outra sequência.

# Ordenando uma lista de tuplas pelo segundo elemento
minha_lista         = [("maçã", 1), ("banana", 3), ("laranja", 2)]
minha_lista_ordenada = sorted(minha_lista, key=lambda x: x[1])
print(minha_lista_ordenada)

# Ordenando um dicionário pelos valores (de maior para menor)
notas = {"Danilo": 7, "Mariana": 9, "Bruno": 4, "Daniela": 8.5}

notas_ordenadas = sorted(
    notas.items(),        # retorna [(chave, valor), ...]
    key=lambda x: x[1],  # ordena pelo valor (segundo elemento de cada tupla)
    reverse=True
)
print(notas_ordenadas)
[('maçã', 1), ('laranja', 2), ('banana', 3)]
[('Mariana', 9), ('Daniela', 8.5), ('Danilo', 7), ('Bruno', 4)]

5.8.3.1 Exemplo: ranking de países por PIB per capita

# (país, PIB per capita em US$ mil, taxa de desemprego em %)
paises = [
    ("Brasil",    9.0,  6.2),
    ("Argentina", 11.6, 7.7),
    ("Chile",     16.5, 8.9),
    ("México",    10.8, 2.7),
    ("Colômbia",   6.8, 9.1),
    ("Peru",       6.6, 7.4),
]

# Por PIB per capita (maior → menor)
ranking_pib = sorted(paises, key=lambda x: x[1], reverse=True)

print("Ranking por PIB per capita (US$ mil):")
for i, (pais, pib, _) in enumerate(ranking_pib, start=1):
    print(f"  {i}º {pais}: US$ {pib:.1f} mil")

print()

# Por taxa de desemprego (menor → maior)
ranking_desemp = sorted(paises, key=lambda x: x[2])

print("Ranking por taxa de desemprego (menor → maior):")
for i, (pais, _, desemp) in enumerate(ranking_desemp, start=1):
    print(f"  {i}º {pais}: {desemp}%")
Ranking por PIB per capita (US$ mil):
  1º Chile: US$ 16.5 mil
  2º Argentina: US$ 11.6 mil
  3º México: US$ 10.8 mil
  4º Brasil: US$ 9.0 mil
  5º Colômbia: US$ 6.8 mil
  6º Peru: US$ 6.6 mil

Ranking por taxa de desemprego (menor → maior):
  1º México: 2.7%
  2º Brasil: 6.2%
  3º Peru: 7.4%
  4º Argentina: 7.7%
  5º Chile: 8.9%
  6º Colômbia: 9.1%
Nota

💡 Sobre a função enumerate()

A função enumerate() permite percorrer uma lista obtendo ao mesmo tempo o índice e o valor de cada elemento. No exemplo, ela é usada para gerar automaticamente a posição no ranking (1º, 2º, etc.), começando em 1 com o parâmetro start=1, sem precisar controlar manualmente um contador.

5.9 Exercícios

  1. Escreva uma função variacao_percentual(valor_inicial, valor_final) que calcule a variação percentual entre dois valores. Aplique-a para calcular a variação do IPCA entre dois anos hipotéticos e a variação do PIB real entre dois trimestres.

  2. Usando as funções calcular_inss e calcular_irpf definidas neste capítulo, escreva uma função aliquota_efetiva(salario_bruto) que retorne a alíquota efetiva total (INSS + IRPF como percentual do salário bruto). Exiba a alíquota efetiva para os salários [2_000, 5_000, 10_000, 20_000] e comente o que você observa.

  3. Escreva uma função elasticidade_preco(q1, q2, p1, p2) que calcule a elasticidade-preço da demanda pela fórmula:

\[\varepsilon = \frac{\Delta Q / Q_1}{\Delta P / P_1}\]

Teste a função com os seguintes dados: quando o preço de um bem passa de R$10 para R$12, a quantidade demandada cai de 100 para 80 unidades. Interprete o resultado.

  1. Considere a lista de países e seus respectivos PIBs per capita (em US$ mil): [("Brasil", 9.0), ("Chile", 16.5), ("Argentina", 11.6), ("México", 10.8), ("Peru", 6.6), ("Colômbia", 6.8)]. Utilizando filter() e map() com funções lambda:

    1. Filtre apenas os países com PIB per capita acima de US$ 10 mil.
    2. Para os países filtrados, crie uma nova lista com o PIB per capita convertido para reais (use a taxa de câmbio de R$ 5,80/US$).
  2. Expanda a função simular_tcl() definida neste capítulo para aceitar também o parâmetro distribuicao, que pode ser "uniforme" ou "exponencial". Use np.random.uniform(0, 1, n) para a distribuição uniforme e np.random.exponential(scale=1, size=n) para a exponencial. Verifique se a média das médias converge para o valor teórico esperado em cada caso (\(\mu = 0{,}5\) para a uniforme e \(\mu = 1\) para a exponencial com scale=1).