Tecnicas Avançadas
Programação Assíncrona
A programação assíncrona permite que o programa não fique parado esperando essas operações terminarem.
Uma corrotina (coroutine) é um objeto que representa uma tarefa assíncrona que pode ser pausada e retomada.
-
async def: Declara que a função será assíncrona. -
await: Diz ao Python para esperar essa operação terminar, sem bloquear o resto do programa. -
asyncio.gather(tarefa1(),tarefa2()): Executa várias tarefas ao mesmo tempo. sem esse método teríamos que esperar uma função terminar antes de chamar a outra, o que anula a vantagem da programação assíncrona. O tempo total de execução é igual ao tempo da tarefa mais longa -
asyncio.run(funcao_assincrona())responsavel por executa-la.asyncsó define a função como assíncrona, mas não a executa, o run que é responsavel por isso. se eu chamar sem o run, retorna um objeto corrotina, mas não inicia a execução da função.
O GIL (Global Interpreter Lock) impede que múltiplas threads usem o processador ao mesmo tempo. Porém, o GIL não afeta processos diferentes! Então, podemos usar multiprocessing para rodar tarefas em paralelo, usando vários núcleos da CPU. Lembrando que o paralelismo consume muitos recursos computacionais, e pode se tornar inviavel.
| Conceito | Como funciona | Exemplo |
|---|---|---|
| Concorrência (asyncio, threads) | Alterna entre tarefas rapidamente | A cpu trabalha em varios processos ao mesmo tempo |
| Paralelismo (multiprocessing) | Executa tarefas ao mesmo tempo, usando vários núcleos do processador | Cada cpu trabalha em um processo separado |
Nesse exemplo, se usassemos funções sincronas, demoraria 11 segundos para que todos os pedidos ficassem prontos. mas demorou apenas 5, pois em 3 o hambuguer e o arroz ficaram prontos, e em mais 2 (5 no total) a pizza.
import asyncio
async def fazer_hamburguer():
print("🍔 Começando a preparar o hambúrguer...")
await asyncio.sleep(3) # Simula o tempo de preparo
print("🍔 Hambúrguer pronto!")
async def fazer_arroz():
print("🍚 Começando a preparar o Arroz...")
await asyncio.sleep(3) # Simula o tempo de preparo
print("🍚 Arroz pronto!")
async def fazer_pizza():
print("🍕 Começando a preparar a pizza...")
await asyncio.sleep(5) # Simula o tempo de preparo
print("🍕 Pizza pronta!")
# Essa Função coordena as tarefas
async def main():
await asyncio.gather(fazer_hamburguer(), fazer_pizza(), fazer_arroz())
print("🛎️ Pedido recebido!")
asyncio.run(main())
print("✅ Pedido finalizado!")
Saída:
🛎️ Pedido recebido!
🍔 Começando a preparar o hambúrguer...
🍕 Começando a preparar a pizza...
🍚 Começando a preparar o Arroz...
🍔 Hambúrguer pronto!
🍚 Arroz pronto!
🍕 Pizza pronta!
✅ Pedido finalizado!
import multiprocessing
import time
def fazer_hamburguer():
print("🍔 Começando a preparar o hambúrguer...")
time.sleep(3)
print("🍔 Hambúrguer pronto!")
def fazer_arroz():
print("🍚 Começando a preparar o arroz...")
time.sleep(3)
print("🍚 Arroz pronto!")
def fazer_pizza():
print("🍕 Começando a preparar a pizza...")
time.sleep(5)
print("🍕 Pizza pronta!")
if __name__ == "__main__":
print("🛎️ Pedido recebido!")
# Criando processos separados
p1 = multiprocessing.Process(target=fazer_hamburguer)
p2 = multiprocessing.Process(target=fazer_arroz)
p3 = multiprocessing.Process(target=fazer_pizza)
# Iniciando os processos (rodando em paralelo)
p1.start()
p2.start()
p3.start()
# Espera todos terminarem
p1.join()
p2.join()
p3.join()
print("✅ Pedido finalizado!")
Assuntos Relacionados
Programação funcional
Programação funcional é um estilo de programação baseado em funções matemáticas e na imutabilidade dos dados.
Isso significa que em vez de alterar variáveis e estados, criamos funções que recebem entradas (inputs) e retornam saídas (outputs) sem efeitos colaterais.
PRINCIPAIS CARACTERISTICAS
✔ Funções como "cidadãos de primeira classe": Isso significa que podemos tratar funções como variáveis: passá-las como argumento, retorná-las e armazená-las em listas ou dicionários.
✔ Imutabilidade (evitar modificar variáveis): Na programação funcional, tentamos não modificar variáveis já criadas, mas sim criar novos valores.
✔ Ausência de efeitos colaterais: Um efeito colateral acontece quando uma função modifica algo fora dela (exemplo: modificar uma variável global, alterar um arquivo no disco, mudar um banco de dados etc.).
✔ Uso de funções puras: Uma função pura é aquela que sempre retorna o mesmo resultado para a mesma entrada e não tem efeitos colaterais.
✔ Uso de funções de alta ordem: São funções que recebem outras funções como argumento ou retornam funções.
Embora seja útil, programação funcional não é ideal para tudo.
❌ Se você precisa modificar estados (bancos de dados, arquivos, variáveis globais, etc.)
❌ Se o código fica mais complicado sem necessidade
❌ Se o desempenho for crítico (Python não otimiza tanto funções puras quanto linguagens como Haskell)
# --- Exemplo 1 --- Cidadãos de primeira classe --- #
# Aqui, a variável mensagem guarda a referência da função saudacao() e pode ser chamada depois.
def saudacao():
return "Olá!"
# Atribuímos a função a uma variável
mensagem = saudacao
print(mensagem()) # Saída: Olá!
# --- Exemplo 2 --- Imutabilidade (Evitar Mudar Variáveis) --- #
# Aqui a lista numeros foi modificada
numeros = [1, 2, 3]
for i in range(len(numeros)):
numeros[i] *= 2
print(numeros) # [2, 4, 6]
# A lista original não foi alterada, apenas criamos uma nova!
numeros = [1, 2, 3]
novos_numeros = list(map(lambda x: x * 2, numeros))
print(novos_numeros) # [2, 4, 6]
# --- Exemplo 3 --- Ausência de Efeitos Colaterais --- #
# Aqui, incrementar() modificou contador, o que pode causar bugs difíceis de rastrear.
contador = 0
def incrementar():
global contador
contador += 1
incrementar()
print(contador) # 1 (modificou uma variável externa)
# Nenhuma variável externa foi alterada! A função apenas recebe um valor e retorna outro.
def incrementar(contador):
return contador + 1
novo_contador = incrementar(0)
print(novo_contador) # 1
# --- Exemplo 4 --- Funções Pura --- #
# Aqui o retorno depende de algo externo (o valor de total muda toda vez que chamamos a função).
total = 0
def somar_e_acumular(a):
global total # Modifica uma variável externa (efeito colateral)
total += a
return total
# Aqui sempre gera o mesmo resultado
def soma(a, b):
return a + b
print(soma(3, 4)) # 7
print(soma(3, 4)) # 7 (sempre o mesmo resultado)
# --- Exemplo 5 --- Funções de Alta Ordem --- #
# Aqui, aplicar_operacao() recebe qualquer função e um número, tornando o código mais flexível.
def aplicar_operacao(funcao, valor):
return funcao(valor)
def quadrado(x):
return x * x
print(aplicar_operacao(quadrado, 5)) # 25
Codificação de Caracteres (Unicode e Charsets)
Unicode é um padrão universal de representação de todos os caracteres, onde cada caractere recebe um número único chamado code point: A → U+0041, ç → U+00E7,🙂 → U+1F642 e etc.
character encodings ou charsets), definem como os caracteres (letras, números, símbolos) são representados internamente em bytes no computador ou transmitidos entre sistemas.| Nome | Caracteres suportados | Tamanho por caractere | Observações |
|---|---|---|---|
| ASCII | 128 caracteres (A–Z, a–z, 0–9, símbolos básicos) | 1 byte (apenas 7 bits usados) | Simples, muito antigo, sem acentos. |
| Latin1 (ISO-8859-1) | 256 caracteres | 1 byte | Inclui caracteres acentuados usados em idiomas da Europa Ocidental (ex: á, ç). |
| UTF-8 | Todos os caracteres Unicode (milhares de idiomas e símbolos) | Variável (1 a 4 bytes) | Compatível com ASCII e recomendado para web. |
Tutórial: Normalização Unicode
# ---- Exemplo do caracter A em unicode usando varias formas ---- #
print("A") # Caractere literal A
print(chr(0x41)) # Usando a função chr com hexadecimal
print(chr(65)) # Usando a função chr com decimal
print("\N{LATIN CAPITAL LETTER A}") # Usando o nome do caractere
print("\u0041") # Usando um hexadecimal 16-bit
print("\U00000041") # Usando um hexadecimal 32-bit
print(ord("A")) # Obtém o valor decimal que representa A
print(hex(ord("A"))) # Obtém o valor hexadecimal que representa A
# ---- ENCODING DECODING ---- #
# Converter string utf-8 para byte utf-8
nome_em_byte = "Diogo Mamédio".encode("utf-8") # noqa: UP012
print(nome_em_byte) # Saída: b'Diogo Mam\xc3\xa9dio'
# Converter byte utf-8 para String utf-8
print(nome_em_byte.decode("utf-8"))
print("b'Diogo Mam\xc3\xa9dio'") # Saída: Diogo Mamédio
Quando você faz print() em um objeto bytes, o Python mostra a representação textual dele, não os caracteres diretamente.