Olá, meu nome é Laís Urano, sou instrutora na Alura e irei acompanhar vocês neste curso de Boas Práticas com Python!
Audiodescrição: Laís se declara uma mulher parda, com olhos e cabelos castanhos, sendo os cabelos cacheados volumosos. Veste uma camisa marrom. Ao fundo, há uma parede branca iluminada com as cores verde e azul.
Este conteúdo é destinado a quem deseja aprender a aplicar boas práticas em seus projetos Python.
Para isso, é necessário ter um conhecimento intermediário na linguagem Python. Também é importante estar familiarizado com os conceitos básicos de criação de API utilizando o framework FastAPI.
Neste curso, aprenderemos a aplicar os princípios de boas práticas da PEP 8, além de desenvolver conceitos e convenções de organização, nomenclatura, tipagem explícita, documentação, compreensão de lista, tratamento de erros e testes automatizados. Aprenderemos tudo isso em um projeto de uma API de recomendação de produtos, de acordo com as preferências e o histórico de uma pessoa usuária.
Aproveitem os recursos da plataforma! Além dos vídeos, temos as atividades, e vocês podem contar com o apoio do fórum e da comunidade da Alura. Vamos lá?
Nós temos o projeto de uma API feita em FastAPI para recomendação de produtos de acordo com as preferências e o histórico de compras de uma determinada pessoa usuária. Esse projeto é de código aberto, ou seja, estará disponível para visualização, modificação e distribuição. Antes de verificar como esse projeto funciona, precisamos executá-lo em nossa máquina.
Estamos utilizando o VSCode, e dentro dele temos quatro arquivos:
.gitignore
app.py
README.md
requirements.txt
Esses arquivos estão disponíveis para download na atividade "Preparando o ambiente".
No nosso caso, temos um arquivo app.py
, que contém todo o código de funcionamento do projeto. Também temos o requirements.txt
, que contém todas as dependências necessárias para o projeto funcionar. O gitignore
é o arquivo que lista todos os arquivos e pastas que não devem ser enviados para o GitHub, como dados sensíveis e informações não pertinentes. Por fim, temos o README.md
, que contém a documentação de funcionamento da aplicação.
É importante acompanhar o README
de acordo com as aulas, pois ele está um pouco mais adiantado em relação ao projeto atual. Vamos verificar apenas pontos específicos conforme a evolução das aulas. O ponto principal a observar é que ele lista as tecnologias utilizadas, principalmente o Python 3.9, necessário para o projeto. Também há algumas dependências importantes mencionadas.
README.md
## código omitido
## Tecnologias Utilizadas
- Python 3.9+
- FastAPI
- Pydantic
- Uvicorn (para rodar o servidor)
- Pytest (para testes automatizados)
## código omitido
Nosso foco agora é a instalação e configuração. O primeiro passo é clonar o repositório ou fazer o download, o que já fizemos. O segundo e terceiro passos tratam de ambientes virtuais: criar e ativar um ambiente.
## código omitido
1. Clone o repositório:
git clone https://github.com/uranolais/boas-praticas-python-curso01.git
cd recomendacao-produtos
2. Crie um ambiente virtual:
python -m venv venv
3. Ative o ambiente virtual:
- No Windows:
venv\Scripts\activate
- No macOS/Linux:
source venv/bin/activate
## código omitido
Vamos abrir o terminal com "Ctrl + J" e executar o seguinte comando:
python -m venv venv
Esse comando cria um ambiente virtual, e podemos perceber que ele cria uma pasta do lado esquerdo chamada venv
. No canto inferior direito, uma mensagem indica que o ambiente foi criado. Vamos fechar essa mensagem por enquanto. Precisamos ativar o ambiente. No Windows, utilizamos:
venv\Scripts\activate
No Linux ou macOS, utilizamos:
source venv/bin/activate
Utilize o comando de acordo com seu sistema operacional.
No nosso caso, estamos utilizando o Windows, então ativamos com venv\Scripts\activate
. Para confirmar a ativação, visualizamos a informação (venv)
entre parênteses antes da linha de diretório no terminal.
Após a ativação, precisamos instalar as dependências necessárias para o funcionamento do projeto com o comando:
A instrutora está seguindo os passos indicados no arquivo
README.md
pip install -r requirements.txt
Teclamos "Enter".
Esse processo pode demorar um pouco, dependendo das dependências a serem instaladas. O pip install
faz a instalação, o -r
lê um arquivo, e o requirements.txt
é o arquivo que contém as dependências a serem lidas. Após a instalação, todas as dependências necessárias para o projeto estão instaladas no ambiente virtual.
Se observarmos o app.py
, ele pode não mostrar que o FastAPI está instalado, mesmo após a instalação. Observe que está com um sublinhado na importação do fastapi
. Para resolver isso, basta reiniciar o VSCode. Ao reiniciar, o VSCode identifica as importações corretamente. No terminal, é exibida a mensagem "Histórico restaurado". Caso isso aconteça, apenas reinicie o VSCode
Criamos e ativamos o ambiente virtual, mas o que é um ambiente virtual e por que utilizamos o venv
especificamente? Existem outros ambientes virtuais que podemos utilizar. Vamos explorar isso.
Abriremos um slide no navegador que mostra um computador com o Python 3.10.11 instalado. Dentro desse computador, podemos criar pequenos computadores.
A função principal de um ambiente virtual é isolar certas dependências para evitar conflitos entre versões de tecnologias. Por exemplo, podemos ter uma máquina com o Python 3.10.11, mas um projeto que requer o Python 3.9 e o Flask 2.3.3. Para evitar conflitos, criamos um ambiente virtual e instalamos as versões necessárias, isolando-as do ambiente global.
Podemos criar diversos ambientes virtualizados, como um com Python e Flask, outro com Python e Django, ou Python com FastAPI, para evitar conflitos entre si.
A função principal do ambiente virtual é isolar dependências para evitar conflitos, uma prática recomendada para pessoas desenvolvedoras de Python.
Utilizamos o venv
porque ele é o ambiente virtual mais utilizado na linguagem Python, com 55% de uso em 2023, segundo o site da JetBrains ao pesquisarmos por "PyCharm Python Survey 2023" no Google. Nesse site, verificamos quais tecnologias são utilizadas no mercado e qual sua frequência de uso.
Ao clicarmos em "Python Packaging" na aba "Contents" verificamos a pesquisa que mostra que o venv
é o mais utilizado na linguagem Python.
Outras opções, como Virtualenv e Conda, tiveram uma queda de uso. O Poetry teve um aumento no uso, no entanto, não é um crescimento tão significativo quanto do venv. Descendo um pouco a página, visualizamos as ferramentas de uso de dependências. No caso a Pip
está no topo com 77%. Quanto ao formato de armazenamento de dependências, visualizamos que o requirements.txt
é o mais comum, com 63% de uso.
A utilização dessas ferramentas deve considerar a preferência pessoal e da empresa.
Voltando ao VSCode, em um ambiente virtual, podemos verificar as dependências necessárias para o projeto com o comando pip freeze
.
pip freeze
annotated-types==0.7.0
anyio==4.6.2.post1
click==8.1.7
colorama==0.4.6
fastapi==0.115.3
h11==0.14.0
idna==3.10
pydantic==2.9.2
pydantic_core==2.23.4
sniffio==1.3.1
starlette==0.41.0
typing_extensions==4.12.2
uvicorn==0.32.0
Esse comando mostra o conteúdo do arquivo do pip requirements.txt
. Podemos atualizar esse arquivo com:
pip freeze > requirements.txt
Assim, mantemos o arquivo atualizado com as dependências atuais do projeto. Se quisermos desinstalar ou instalar uma tecnologia, podemos atualizar o requirements.txt
com pip freeze
para garantir que o projeto funcione corretamente.
Agora que instalamos todas as dependências e o app.py
as reconhece, podemos verificar como o projeto funciona e quais pontos precisamos trabalhar. É isso que faremos a seguir!
Instalamos as dependências necessárias para o projeto rodar e configuramos um ambiente virtual para sua execução. Agora, é importante compreender como ele está estruturado e como podemos interagir com ele.
O primeiro ponto a ser analisado no arquivo app.py
são os imports:
from pydantic import BaseModel
from typing import List
from fastapi import FastAPI
from fastapi import APIRouter, HTTPException
# código omitido
Os imports
são responsáveis por trazer os módulos e bibliotecas essenciais para o funcionamento da nossa aplicação. Incluímos o import de modelo, o import de typing
para tipos, como List
, e também os imports do FastAPI
, framework utilizado para a construção da nossa API.
A partir da linha 9, temos a criação de modelos.
Modelos são representações de dados que serão utilizados dentro da nossa aplicação.
# código omitido
# Modelo base para produto
class ProdutoBase(BaseModel):
nome: str
categoria: str
tags: List[str]
# Modelo para criar um produto
class CriarProduto(ProdutoBase):
pass
# Modelo de produto com ID
class Produto(ProdutoBase):
id: int
# Modelo para histórico de compras do usuário
class HistoricoCompras(BaseModel):
produtos_ids: List[int]
# Modelo para preferências do usuário
class Preferencias(BaseModel):
categorias: List[str] | None = None
tags: List[str] | None = None
# código omitido
No momento, estamos criando um tipo de dado, por exemplo, o ProdutoBase()
, que terá nome
, categoria
e tags
, todos eles do tipo string
. O ProdutoBase()
herda do BaseModel
, que é do próprio pydantic
.
Em seguida, temos o CriarProduto()
, que é o modelo para a criação de um produto, herdando do ProdutoBase
, que é o primeiro modelo que verificamos. Logo após, temos o modelo de Produto()
, que também herda do ProdutoBase
e cria um id
do tipo inteiro
para o produto.
Logo após, temos um modelo para o histórico de compras da pessoa usuária. O HistoricoCompras()
herda do BaseModel
, com produtos_ids
sendo uma lista de inteiros que representam os IDs dos produtos. Por último, temos a classe Preferencias()
, que é o modelo de preferências, herdando do BaseModel
, com categorias
e tags
, que são listas de strings
ou vazias (None
).
Para representar a pessoa usuária, definimos a classe Usuario()
, que serve como modelo de dados. Esta classe herda de BaseModel
e define dois atributos: um id
do tipo inteiro e um nome
do tipo string:
# código omitido
# Modelo base para um usuário
class Usuario(BaseModel):
id: int
nome: str
# código omitido
Estamos abordando de forma superficial o funcionamento deste projeto, pois já vamos trabalhar com ele em funcionamento.
Caso prefira, há uma atividade explicando detalhadamente como o projeto foi criado e como funciona.
Em seguida, definimos algumas variáveis essenciais para o funcionamento do nosso projeto: Produtos
, que é uma lista; Usuarios
, igualmente uma lista; ContadorProduto
e ContadorUsuario
, ambos iniciados como inteiros com valor 1
.
Também temos a constante ConstanteMensagemHome
, que armazena uma mensagem de boas-vindas à API de recomendação de produtos, e o HistoricoDeCompras
, um dicionário em memória. Por fim, criamos uma instância de app
, que é inicializada na linha 57 com app = FastAPI()
:
# código omitido
Produtos =[]
ContadorProduto =1
Usuarios =[]
ContadorUsuario =1
ConstanteMensagemHome ="Bem-vindo à API de Recomendação de Produtos"
# Histórico de compras em memória
HistoricoDeCompras ={}
# Criando o App
app = FastAPI()
# código omitido
Estamos criando uma instância desse app FastAPI.
Para iniciar o servidor e definir as rotas da API, utilizamos o decorador @app.get("/")
(sendo get
o recurso HTTP), passando como argumento a rota ou endpoint que desejamos acessar. Neste caso, estamos criando o endpoint /
, que é o principal da nossa aplicação. Em seguida, definimos uma função que será executada quando a rota for acessada:
# código omitido
@app.get("/")
def home():
global ConstanteMensagemHome
return {"mensagem": ConstanteMensagemHome}
# código omitido
A função home()
, por exemplo, retorna a constante ConstanteMensagemHome
, que contém a mensagem de boas-vindas à API de recomendação de produtos. É fundamental lembrar que toda rota deve ter um endpoint associado e deve sempre retornar uma resposta.
Depois, temos a rota de POST
de produtos, que cria um produto do modelo Produto
:
# código omitido
# Rota para cadastrar produtos
@app.post("/produtos/", response_model=Produto)
def criarproduto(produto: CriarProduto):
global ContadorProduto
NovoProduto = Produto(id=ContadorProduto, **produto.model_dump())
Produtos.append(NovoProduto)
ContadorProduto += 1
return NovoProduto
# Rota para listar todos os produtos
@app.get("/produtos/", response_model=List[Produto])
def listarprodutos():
return Produtos
# código omitido
A função recebe o produto criado e retorna exatamente o valor que foi fornecido. Além disso, é possível acessar esses produtos através da rota /produtos
, que agora é uma requisição do tipo GET
e retorna a lista completa de produtos cadastrados.
Na sequência, temos uma rota de POST
para o histórico de compras, que cria o histórico de compras para a pessoa usuária:
# código omitido
# Rota para simular a criação do histórico de compras de um usuário
@app.post("/historico_compras/{usuario_id}")
def adicionarhistoricocompras(usuario_id: int, compras: HistoricoCompras):
if usuario_id not in [usuario.id for usuario in Usuarios]:
raise HTTPException(status_code=404, detail="Usuário não encontrado")
HistoricoDeCompras[usuario_id] = compras.produtos_ids
return {"mensagem": "Histórico de compras atualizado"}
# código omitido
Primeiro, verifica se a pessoa usuária existe. Se existir, cria o histórico de compras e retorna uma mensagem de "Histórico de compras atualizado".
Em seguida, temos a rota POST
de recomendações, que verifica se a pessoa usuária informado possui um histórico de compras já que recebe usuario_id
e as preferencias
da pessoa usuária, e, caso exista um histórico de compras, retorna os produtos_recomendados
, filtrando-os por categoria e tags de preferência:
# código omitido
# Rota para recomendações de produtos
@app.post("/recomendacoes/{usuario_id}", response_model=List[Produto])
def recomendarprodutos(usuario_id: int, preferencias: Preferencias):
if usuario_id not in HistoricoDeCompras:
raise HTTPException(status_code=404, detail="Histórico de compras não encontrado")
produtos_recomendados = []
# Buscar produtos com base no histórico de compras do usuário
produtos_recomendados = [produto for produto_id in HistoricoDeCompras[usuario_id] for produto in Produtos if produto.id == produto_id]
# Filtrar as recomendações com base nas preferências
produtos_recomendados = [p for p in produtos_recomendados if p.categoria in preferencias.categorias] # Preferencias de categorias
produtos_recomendados = [p for p in produtos_recomendados if any(tag in preferencias.tags for tag in p.tags)] # Preferencias de tags
return produtos_recomendados
# código omitido
Por fim, temos as rotas relacionadas às pessoas usuárias:
# código omitido
@app.post("/usuarios/", response_model=Usuario)
def criarusuario(nome: str):
global ContadorUsuario
NovoUsuario = Usuario(id=ContadorUsuario, nome=nome)
Usuarios.append(NovoUsuario)
ContadorUsuario += 1
return NovoUsuario
# Rota para listar usuários
@app.get("/usuarios/", response_model=List[Usuario])
def listarusuarios():
return Usuarios
# código omitido
A primeira é uma rota POST
, semelhante à de produtos, que cria um usuário a partir do modelo Usuario
e retorna o usuário criado. Também existe a rota app.get()
para /usuarios
, que retorna a lista completa de pessoas usuárias cadastrados.
Em resumo, é assim que nosso projeto está organizado e estruturado.
Para rodar o projeto, abrimos o terminal utilizando o atalho "Ctrl + J". Para iniciar o servidor, utilizamos o comando uvicorn
, que é o servidor responsável pela execução do projeto. O comando a ser utilizado é:
uvicorn app:app --reload
O primeiro app
do comando é o nome do arquivo python, os :app
é o nome da instância que criamos dentro da aplicação e o reload
serve para reinicializar de forma automática.
Teclamos "Enter" para rodar e obtemos o retorno a seguir:
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [16744] using StatReload
INFO: Started server process [8196]
INFO: Waiting for application startup.
INFO: Application startup complete.
O uvicorn verifica se o projeto está correto, ou seja, se não há erros que possam impedir seu funcionamento. Ele então informa que o servidor está em execução na porta 8000
. Pressionamos "Ctrl" e clicamos no link fornecido para ser redirecionado automaticamente para o navegador.
Endereço: 127.0.0.1:8000
{"mensagem": "Bem-vindo à API de Recomendação de Produtos"}
No navegador, a primeira página é a home (podemos inserir uma barra no final do endereço para confirmar), que é o endpoint principal, onde recebemos a mensagem de boas-vindas.
Para verificar e trabalhar com a API, o FastAPI disponibiliza uma documentação em /docs
:
127.0.0.1:8000/docs
Nela, temos acesso a todas as rotas criadas na API e todos os schemas ou modelos criados na aplicação.
Primeiro, clicamos em GET /Home
, abaixamos a seta à direita, observe que temos informações como parâmetros. Para testar a aplicação, clicamos no botão "Try it out" à direita. Em "Servers" temos o servidor, mais abaixo clicamos no botão "Execute" na parte inferior central. Ele retorna a resposta, informando a URL solicitada e a resposta recebida, que foi um código 200
, a mensagem de boas-vindas e o response headers:
Curl
curl -X 'GET' \
-H 'accept: application/json'
Request URL
Response body
{
"mensagem": "Bem-vindo à API de Recomendação de Produtos"
}
Response headers
content-length: 61
content-type: application/json
date: Mon, 28 Oct 2024 18:02:56 GMT
server: uvicorn
Mais abaixo, informa as possíveis respostas, no caso é somente o 200.
Para fechar a aba do GET /Home
, clicamos na seta à direita novamente. Agora, vamos testar o funcionamento da aplicação.
Primeiro, criamos um produto. Clicamos na seta da aba POST /produtos/ Criarproduto
, depois no botão "Try it out", e organizamos o JSON com os dados do produto. Por exemplo:
{
"nome": "celular",
"categoria": "eletrônico",
"tags":
["novo", "iphone"
]
}
Clicamos em "Execute" e recebemos a resposta com código 200 e o produto criado no responde body mais abaixo:
{
"nome": "celular",
"categoria": "eletrônico",
"tags": [
"novo",
"iphone"
],
"id": 1
}
Criaremos outro produto, um fone:
{
"nome": "fone",
"categoria": "eletrônico",
"tags":
["novo", "jbl"
]
}
Enviamos novamente clicando em "Execute". O ID muda conforme criamos novos produtos:
{
"nome": "fone",
"categoria": "eletrônico",
"tags": [
"novo",
"jbl"
],
"id": 2
}
Conforme criamos mais produtos, o id mudou.
Podemos listar os produtos salvos ao clicar na seta da aba GET /produtos/ Listarprodutos e, em seguida, selecionar o botão "Try it out" e clicar em "Execute". A partir disso, será exibida a lista de produtos, contendo os dois produtos previamente criados, acompanhados dos respectivos IDs:
[ {
"nome": "celular",
"categoria": "eletrônico",
"tags": [
"novo",
"iphone"
],
"id": 1
}, {
"nome": "fone",
"categoria": "eletrônico",
"tags": [
"novo",
"jbl"
],
"id": 2
}
]
Fechamos a aba clicando na seta.
Para registrar o histórico, que requer o parâmetro usuario_id
, é necessário primeiro criar um usuário. No POST
de /usuarios/ Criarusuario
, clicamos no botão "Try it out", inserimos, por exemplo, o nome "Laís" no "nome".
Executamos a ação clicando no botão "Execute". O sistema criará um usuário com ID 1 e o nome "Laís" no "Response body" mais abaixo:
{
"id": 1,
"nome": "lais"
}
Para confirmar a criação, podemos listar as pessoas usuárias expandindo a aba GET /usuarios/ Listarusuarios
e verificar a inclusão do novo registro clicando em "Try it out" e depois em "Execute". Descendo um pouco a página, temos:
[
{
"id": 1,
"nome": "lais"
}
]
Confirmamos que foi criado com sucesso, podemos fechar essa aba.
Agora, realizamos os testes das recomendações. Primeiramente, criamos um histórico de compras. Na aba POST
destinada ao histórico de compras, clicamos no botão "Try it out", inserimos o usuario_id
como 1
e o produto_id
como 1
no json:
{
"produtos_ids": [
]
}
Executamos a ação clicando em "Execute". Como resposta, recebemos a mensagem "Histórico de compras atualizado" no response body. Isso significa que a informação foi salva.
Em seguida, na aba POST
das recomendações (POST /recomendacoes/{usuario_id} Recomendarprodutos
), novamente selecionamos o botão "Try it out", inserimos o usuario_id
como 1
, além das categorias "eletrônico" e das tags "novo" no json:
{
"categorias": [
"eletrônico"
],
"tags": [
"string"
]
}
Clicamos em "Execute". Lembrando que compramos apenas um celular. No response body, a recomendação recebida foi de um celular, que correspondia ao produto previamente criado:
{
"nome": "celular",
"categoria": "eletrônico",
"tags": [
"novo",
"iphone"
],
"id": 1
}
]
Através desses testes, conseguimos compreender o funcionamento da API, observando o fluxo de ações: criação de produtos, cadastro de usuário, registro de histórico de compras e, finalmente, a geração das recomendações.
No VSCode, todas as informações de requisições GET
e POST
são exibidas no terminal, incluindo as respostas 200 OK
e as rotas acessadas:
Retorno abaixo parcialmente transcrito:
INFO: 127.0.0.1:62019 - "GET / HTTP/1.1" 200 OK
INFO: 127.0.0.1:62019 - "GET /favicon.ico HTTP/1.1" 404 Not Found
INFO: 127.0.0.1:62020 - "GET / HTTP/1.1" 200 OK
INFO: 127.0.0.1:62021 - "GET /docs HTTP/1.1" 200 OK
INFO: 127.0.0.1:62021 - "GET /openapi.json HTTP/1.1" 200 OK
Fechamos o terminal com o atalho "Ctrl + J". Ao fazer isso, notamos que o código está um pouco desorganizado, com várias variáveis, como modelos e rotas, agrupadas sem seguir um padrão claro de estrutura ou nomenclatura.
Nosso objetivo é melhorar o projeto, aplicando boas práticas e reorganizando o código para aumentar sua legibilidade e facilitar contribuições, já que é um projeto de código aberto. A partir daqui, começaremos esse processo de melhoria!
O curso Python: aplicando boas práticas com PEP 8 possui 138 minutos de vídeos, em um total de 46 atividades. Gostou? Conheça nossos outros cursos de Python em Programação, ou leia nossos artigos de Programação.
Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:
Impulsione a sua carreira com os melhores cursos e faça parte da maior comunidade tech.
1 ano de Alura
Assine o PLUS e garanta:
Formações com mais de 1500 cursos atualizados e novos lançamentos semanais, em Programação, Inteligência Artificial, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.
A cada curso ou formação concluído, um novo certificado para turbinar seu currículo e LinkedIn.
No Discord, você tem acesso a eventos exclusivos, grupos de estudos e mentorias com especialistas de diferentes áreas.
Faça parte da maior comunidade Dev do país e crie conexões com mais de 120 mil pessoas no Discord.
Acesso ilimitado ao catálogo de Imersões da Alura para praticar conhecimentos em diferentes áreas.
Explore um universo de possibilidades na palma da sua mão. Baixe as aulas para assistir offline, onde e quando quiser.
Acelere o seu aprendizado com a IA da Alura e prepare-se para o mercado internacional.
1 ano de Alura
Todos os benefícios do PLUS e mais vantagens exclusivas:
Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos, corrige exercícios e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com a Luri até 100 mensagens por semana.
Aprenda um novo idioma e expanda seus horizontes profissionais. Cursos de Inglês, Espanhol e Inglês para Devs, 100% focado em tecnologia.
Transforme a sua jornada com benefícios exclusivos e evolua ainda mais na sua carreira.
1 ano de Alura
Todos os benefícios do PRO e mais vantagens exclusivas:
Mensagens ilimitadas para estudar com a Luri, a IA da Alura, disponível 24hs para tirar suas dúvidas, dar exemplos práticos, corrigir exercícios e impulsionar seus estudos.
Envie imagens para a Luri e ela te ajuda a solucionar problemas, identificar erros, esclarecer gráficos, analisar design e muito mais.
Escolha os ebooks da Casa do Código, a editora da Alura, que apoiarão a sua jornada de aprendizado para sempre.