Funções
Repetir o mesmo bloco de código em vários lugares do programa é um convite a bugs: no dia em que precisar corrigir algo, você vai ter que lembrar de mudar em todos os lugares. Funções resolvem isso empacotando um comportamento com um nome, para ser chamado quantas vezes for preciso.
Definindo e chamando funções
Seção intitulada “Definindo e chamando funções”Uma função é definida com def, pode receber parâmetros e devolver um resultado com return:
def saudacao(nome: str) -> str: return f"Olá, {nome}!"
mensagem = saudacao("Ana")print(mensagem) # Olá, Ana!O código dentro da função só executa quando ela é chamada (com ()), não quando é definida. Se não houver return, a função devolve None implicitamente:
def imprime_log(texto: str) -> None: print(f"[LOG] {texto}")
resultado = imprime_log("iniciando")print(resultado) # NoneUma docstring (string logo abaixo do def, entre """) documenta o que a função faz, e ferramentas como help() e editores de código leem esse texto:
def area_circulo(raio: float) -> float: """Calcula a área de um círculo a partir do raio.""" return 3.14159 * raio ** 2Parâmetros
Seção intitulada “Parâmetros”Posicionais e nomeados
Seção intitulada “Posicionais e nomeados”Por padrão, os argumentos são associados aos parâmetros pela ordem (posicionais). Mas também é possível nomear o argumento na chamada, o que deixa claro o que cada valor representa:
def apresenta(nome: str, idade: int) -> str: return f"{nome}, {idade} anos"
apresenta("Ana", 25) # posicionalapresenta(nome="Ana", idade=25) # nomeadoapresenta(idade=25, nome="Ana") # nomeado, ordem não importaValores padrão
Seção intitulada “Valores padrão”Um parâmetro pode ter um valor padrão, usado quando a chamada não informa aquele argumento:
def saudacao(nome: str, saudacao_inicial: str = "Olá") -> str: return f"{saudacao_inicial}, {nome}!"
saudacao("Ana") # Olá, Ana!saudacao("Ana", "Boa tarde") # Boa tarde, Ana!Uma armadilha clássica: nunca use uma lista, dicionário ou outro objeto mutável como valor padrão. Esse valor é criado uma única vez, quando a função é definida, e compartilhado entre todas as chamadas que não informam esse argumento:
# ERRADO: o valor padrão "vaza" entre chamadas diferentesdef adiciona_item(item, lista=[]): lista.append(item) return lista
adiciona_item("a") # ['a']adiciona_item("b") # ['a', 'b'] -> a lista anterior não foi reiniciada!
# CORRETO: use None como padrão e crie a lista dentro da funçãodef adiciona_item(item, lista=None): if lista is None: lista = [] lista.append(item) return lista*args e **kwargs
Seção intitulada “*args e **kwargs”Quando o número de argumentos não é fixo, *args recolhe os argumentos posicionais extras em uma tupla, e **kwargs recolhe os argumentos nomeados extras em um dicionário:
def soma(*numeros: float) -> float: return sum(numeros)
soma(1, 2, 3) # 6soma(1, 2, 3, 4, 5) # 15
def cria_perfil(**dados): for chave, valor in dados.items(): print(f"{chave}: {valor}")
cria_perfil(nome="Ana", idade=25, cidade="Fortaleza")Os nomes args e kwargs são só convenção - o que importa são os operadores * e **. Quando parâmetros são combinados, a ordem obrigatória é: posicionais, depois *args, depois nomeados com padrão, depois **kwargs:
def funcao(a, b, *args, c=10, **kwargs): print(a, b, args, c, kwargs)
funcao(1, 2, 3, 4, c=20, d=30)# 1 2 (3, 4) 20 {'d': 30}Funções como objetos
Seção intitulada “Funções como objetos”Em Python, uma função é um valor como qualquer outro - pode ser guardada em uma variável, passada como argumento ou retornada por outra função.
Uma lambda é uma função anônima, de uma única expressão, útil quando você precisa de uma função pequena e descartável, geralmente como argumento de outra função:
dobro = lambda x: x * 2dobro(5) # 10
# equivalente a:def dobro(x): return x * 2lambda não aceita várias linhas nem return explícito - se a lógica for mais complexa que uma expressão simples, use def normalmente.
Funções de alta ordem
Seção intitulada “Funções de alta ordem”Funções que recebem outra função como argumento são chamadas de alta ordem. sorted(), map() e filter() são os exemplos mais comuns:
pessoas = [("Ana", 25), ("Bruno", 19), ("Carla", 30)]
# ordena pela idade (segundo item da tupla)por_idade = sorted(pessoas, key=lambda p: p[1])
# aplica uma função a cada itemdobros = list(map(lambda x: x * 2, [1, 2, 3])) # [2, 4, 6]
# filtra itens que satisfazem uma condiçãopares = list(filter(lambda x: x % 2 == 0, range(10))) # [0, 2, 4, 6, 8]Escopo é a região do código onde um nome (variável, função) pode ser acessado.
x = 10 # escopo global
def minha_funcao(): y = 20 # escopo local, só existe dentro da função print(x) # pode ler variável global print(y)
minha_funcao()print(x) # 10, funcionaprint(y) # NameError: y só existe dentro de minha_funcaoPor padrão, uma função só lê variáveis do escopo externo - para reatribuir uma variável global dentro de uma função, é preciso declarar global explicitamente:
contador = 0
def incrementa(): global contador contador += 1
incrementa()print(contador) # 1nonlocal faz o mesmo papel, mas para o escopo de uma função que envolve outra (closures, a seguir), não o escopo global.
Closures
Seção intitulada “Closures”Uma closure acontece quando uma função interna “lembra” de variáveis do escopo da função externa, mesmo depois que a função externa já terminou de executar:
def cria_contador(): contagem = 0
def incrementar(): nonlocal contagem contagem += 1 return contagem
return incrementar
contador = cria_contador()contador() # 1contador() # 2contador() # 3Cada chamada a cria_contador() gera uma closure independente, com sua própria cópia de contagem - é assim que decorators (assunto para quando você chegar na seção Python) conseguem “lembrar” de configurações entre chamadas.
Retorno de múltiplos valores
Seção intitulada “Retorno de múltiplos valores”Uma função Python só retorna um valor, mas esse valor pode ser uma tupla - o que, na prática, dá a sensação de retornar vários valores de uma vez:
def divide_com_resto(a: int, b: int) -> tuple[int, int]: return a // b, a % b
quociente, resto = divide_com_resto(17, 5)print(quociente, resto) # 3 2O desempacotamento (quociente, resto = ...) funciona porque a tupla retornada tem exatamente dois itens, e eles são atribuídos às duas variáveis na mesma ordem.