Aprender Python num fim de semana: De zero a um projeto completo
Como podemos aprender rapidamente uma nova linguagem de programação? Neste artigo, vamos partilhar a nossa experiência de fim de semana ao aprender Python construindo um projeto completo.
Introdução
O Logto, como um serviço de identidade, é essencial para fornecer uma experiência fluida em várias linguagens de programação e frameworks. Isso frequentemente envolve a criação de Kits de Desenvolvimento de Software (SDKs). No entanto, quando a linguagem de programação está fora do nosso stack tecnológico e a nossa equipa não está familiarizada com ela, criar um SDK robusto torna-se um desafio.
Python colocou-nos esse desafio. Apesar de termos muitos utilizadores de Python, faltava-nos um SDK de Python, o que era uma preocupação persistente. Determinado em resolver isso, comecei a preencher esta lacuna.
Apesar de ter anos de experiência em programação, Python continua a ser um território relativamente desconhecido para mim. Enquanto brinquei brevemente com o Python 2 para scripts simples anos atrás, o meu conhecimento estava desatualizado. No entanto, era hora de mergulhar!
Dia 1: Lançar os alicerces
Definir o objetivo
Na minha experiência, a abordagem mais eficaz para aprender uma nova linguagem de programação é construir um projeto completo. Felizmente, o nosso objetivo era claro: construir um SDK de Python para o Logto destinado a aplicações web.
Em vez de saltar diretamente para o código, vamos dividir o processo. Aqui está a lista:
- Criar o cliente Logto para tarefas como login, logout, informações de utilizador e gestão de tokens.
- Fornecer um tutorial e um projeto de exemplo para demonstrar o uso do SDK.
- Publicar o SDK em algum lugar para que os utilizadores possam instalá-lo facilmente.
Parece que a tarefa 1 tem a maior carga de trabalho, por isso precisamos de confirmar o escopo e continuar a dividi-la. Este passo é crucial para garantir os limites do projeto e evitar o aumento do escopo e sobreengenharia.
Trabalhos anteriores da nossa equipa pouparam-me uma tonelada de tempo:
- Uma convenção de SDK delineou a estrutura e o design das APIs dos SDKs.
- SDKs existentes para diferentes linguagens forneceram percepções sobre padrões e potenciais melhorias para Python.
Referenciando esses recursos, consigo ter uma ideia clara do que fazer. Embora os detalhes estejam além do escopo deste artigo, vamos prosseguir.
Configuração do ambiente
Estou a usar um Mac, logo Python já está instalado. No entanto, estava a pensar se há uma maneira melhor de gerir as versões do Python (já ouvi falar das dores com compatibilidade de versões), semelhante ao nvm
para Node.js. Rapidamente encontrei o pyenv e comecei a instalá-lo diretamente.
O próximo ponto na agenda: um gestor de pacotes e dependências. Normalmente, eles andam de mãos dadas. Então, por que não pip
(o padrão do Python)? Se olhares para o requirements.txt
, vais ver que é apenas uma lista de pacotes com versões. Isso não é suficiente para um SDK que pode ser usado por outros projetos. Por exemplo, podemos precisar adicionar alguns pacotes para desenvolvimento, mas não queremos incluí-los no SDK final. O requirements.txt
é demasiado simples para lidar com isso.
Um dos benefícios de ter outras linguagens de programação no teu stack tecnológico é que podes procurar pelo "equivalente em Python". Assim, procurei por "equivalente ao package.json no Python" e encontrei o Poetry, um candidato notável:
- Funciona como um gestor de pacotes, gestor de dependências e gestor de ambientes virtuais.
- Possui um ficheiro
pyproject.toml
, semelhante aopackage.json
no Node.js. - Emprega um ficheiro de lock para registrar versões precisas das dependências.
CLIs modernos muitas vezes incluem um comando init
adaptado para novos projetos. O Poetry também tem. Executei o comando e ele criou um ficheiro pyproject.toml
para mim.
A primeira linha de código
Finalmente, havia chegado o momento de escrever código. Iniciar com o clássico programa "Hello, World!" é sempre uma boa escolha. Quando estamos a aprender uma linguagem de programação, IDEs completos nem sempre são essenciais; um editor com uma comunidade forte, como o VS Code, é completamente adequado.
Dado que o nosso SDK é focado em aplicações web, comecei por criar um servidor web simples utilizando o popular framework Flask.
Aproveitando as capacidades do Poetry, instalar o Flask pode ser facilmente feito ao correr poetry add flask
. Depois, seguindo o guia oficial de início rápido do Flask, compus um ficheiro 'hello.py' com o seguinte trecho:
Executando o servidor via flask --app hello run
e navegando para http://localhost:5000 no meu navegador, obtive o resultado desejado. Funcionou!
Como iniciante, não estava com pressa de escrever mais código. Em vez disso, há muita informação para absorver no trecho de código:
- Usar
from x import y
para importar um módulo ou uma classe. - Não há ponto e vírgula para terminar linhas (oh não).
- Podemos definir uma nova variável simplesmente dando-lhe um nome arbitrário e atribuindo-lhe um valor.
- Criar uma instância de uma classe sem a palavra-chave
new
. - Python suporta decoradores, e o comando
@app.route
serve como um decorador que regista uma função como manipulador de rotas.- O valor retornado pela função é interpretado como o corpo da resposta.
- Podemos definir uma função usando a palavra-chave
def
.
Como podes ver, se tentarmos compreender cada linha de código em vez de "simplesmente fazer funcionar", podemos aprender muito com ela. Entretanto, a documentação oficial do Flask explicou ainda mais o trecho em detalhe.
Iniciar o projeto
Agora é hora de começar o projeto. Rapidamente defini uma classe LogtoClient
e tentei adicionar algumas propriedades e métodos para sentir a linguagem:
Subsequentemente, integrei a classe com o Flask:
Começou a parecer um projeto real. Mas senti que algo estava a faltar: um sistema de tipos.
Sistema de tipos
Como é um SDK, incorporar um sistema de tipos ajudará os utilizadores a entender a API e reduzir a chance de erros durante o desenvolvimento.
O Python introduziu type hints na versão 3.5. Não é tão poderoso quanto o TypeScript, mas é melhor do que nada. Adicionei algumas dicas de tipos à classe LogtoClient
:
Parece muito melhor agora. Mas o desafio surge quando se trata de um tipo complexo, como um objeto com chaves predefinidas. Por exemplo, precisamos definir uma classe LogtoConfig
para representar o objeto config:
Parece correto, mas em breve teremos que enfrentar os problemas de codificação, descodificação e validação do objeto a partir de JSON.
Depois de alguma pesquisa, escolhi pydantic como a solução. É uma biblioteca de validação de dados que funciona com dicas de tipos. Suporta diversas funcionalidades de JSON sem seguir código clichê e tedioso.
Assim, a classe LogtoConfig
pode ser reescrita como:
Também me ensinou sobre herança de classes em Python, adicionando parêntesis após o nome da classe.
Operações assíncronas
No SDK do Logto, precisamos fazer pedidos HTTP ao servidor do Logto. Se tiveres alguma experiência com JavaScript, a frase "callback hell" provavelmente soa familiar. É um problema comum ao lidar com operações assíncronas. Linguagens de programação modernas apresentam soluções semelhantes, como Promise
ou coroutine
.
Felizmente, Python tem uma solução incorporada de async
e await
. Antes de os utilizar, é preciso garantir a compatibilidade com os frameworks populares. No Flask, isso pode ser feito ao instalar o extra async
e usando async def
em vez de def
:
Depois podemos usar await
para esperar pelo resultado de uma operação assíncrona.
Pedidos HTTP
Pedidos HTTP são um tópico interessante. Quase todas as linguagens de programação têm uma solução nativa, mas os programadores geralmente usam uma biblioteca de terceiros para facilitar o uso. Alguns exemplos:
- JavaScript:
XMLHttpRequest
vs.fetch
vs.axios
- Swift:
URLSession
vs.Alamofire
- Java:
HttpURLConnection
vs.OkHttp
Isso também é verdade para Python. A minha decisão foi usar aiohttp porque suporta async
e await
, além de ser popular.
A magia do Copilot
Antes do Copilot, agora chegávamos à parte tediosa de escrever a lógica de negócios. Com a ajuda da convenção de SDK e outros SDKs, consigo escrever os comentários descritivos para cada método antes de escrever o código.
Isto aumenta a legibilidade do código e também ajuda os desenvolvedores a perceber a API diretamente no IDE ou editor por meio da inteligência de código.
Por exemplo, considere o método generateCodeChallenge
, os comentários podem ser escritos assim:
Isto colocou um ótimo prompt para Modelos de Linguagem de Grande Escala (LLMs): compreendendo definições de métodos por meio de comentários claros. E o Copilot não decepcionou:
Alguns ajustes podem ser necessários, mas não importa. Já mudou o jogo.
Conclusão
Isso basicamente resume o progresso feito no primeiro dia. Foi um dia longo, mas com ferramentas e tecnologias modernas, foi bem melhor do que eu esperava.
Dia 2: Elevar o patamar
Com base no trabalho do primeiro dia, a lógica de negócios foi concluída rapidamente. Porém, para um SDK, ainda é insuficiente. Aqui estão as tarefas para o segundo dia:
- Adicionar testes unitários.
- Aplicar formatação de código.
- Verificar a compatibilidade com diferentes versões de Python.
- Adicionar integração contínua.
- Publicar o SDK.
Testes unitários
Testes unitários salvaram-nos várias vezes, então eu não vou pular isso. Aqui estão considerações comuns ao escrever testes unitários:
- Como organizar e executar os testes?
- Como verificar o resultado?
- Como executar testes assíncronos? (Parece simples, mas ocasionalmente pode dar problemas em algumas linguagens.)
- Como simular as dependências? (Não te aprofunda nesse tópico até que realmente seja indispensável, pode ser uma toca de coelho.)
- Como gerar relatórios de cobertura de código?
Com essas perguntas em mente, percebi que o módulo incorporado unittest
não atendia a alguns casos. Então escolhi o pytest como o framework de testes. Suporta testes assíncronos e parece suficientemente maduro.
A jornada revelou-me alguns conceitos novos interessantes, como fixture
. Isso também pode beneficiar a mentalidade ao escrever código noutras linguagens.
Formatação de código
Cada linguagem tem os seus próprios estilos de formatação de código. Pessoalmente, uma formatação consistente deixa-me feliz e confortável; também é útil para revisão de código e colaboração.
Em vez de pesquisar o argumento do "melhor" estilo, decidi escolher um formatador opinativo e aderir a ele.
Black parece ser uma boa escolha. A única frustração é o tamanho das tabs ser imutável. Mas não é grande coisa; optei por me adaptar a isso.
Compatibilidade com diferentes versões de Python
Como um SDK, deve ser compatível com as versões mais utilizadas de Python. Ao pesquisar "estatísticas de uso das versões de Python", determinei que usarei Python 3.8 como a versão mínima.
Agora surge a virtude de um gestor de ambiente. Posso alternar facilmente a versão do Python ao correr pyenv local 3.8
e poetry env use 3.8
. Depois, poderia executar os testes para identificar problemas de compatibilidade.
Integração contínua
A integração contínua garante a qualidade de cada alteração de código. Como o nosso repositório estava hospedado no GitHub, o GitHub Actions foi a escolha natural.
O fluxo de trabalho principal segue princípios simples:
- Configurar o ambiente.
- Instalar as dependências.
- Compilar o projeto (Não é necessário para Python, no entanto).
- Executar os testes.
O GitHub Actions tem uma boa comunidade, então só demorou alguns minutos para construir o workflow.
Ao usar estratégias de matriz, podemos executar o workflow em diferentes versões de Python, até mesmo em diferentes sistemas operativos.
Publicar o SDK
O último passo é publicar o SDK. Para pacotes públicos, isso geralmente pode ser feito ao submeter ao registo oficial de pacotes específicos da linguagem. Por exemplo, npm para Node.js, PyPI para Python, e CocoaPods para Swift.
O Poetry é a minha estrela guia. Basta correr poetry publish
para publicar o pacote no PyPI. É tão simples como isso.
Reflexões finais
Foi uma jornada envolvente. Sem a ajuda da comunidade open-source, teria sido muito mais difícil. Aplaudo todos os colaboradores!
Aqui estão alguns aspetos gerais a reter:
- Defina o objetivo com precisão e divida-o, depois mantenha sempre o objetivo em mente.
- Configure um ambiente de desenvolvimento estável e reproduzível.
- Use (bons) ferramentas sempre que possível.
- Priorize soluções incorporadas ou já existentes.
- Compreenda as convenções da linguagem e cada linha de código que escreves.
- No entanto, não fiques obcecado com minúcias.
- Use o Copilot para tarefas claras e descritivas.
Podes encontrar o resultado final neste repositório. Com a mesma estratégia, também construí rapidamente o Logto PHP SDK. Não hesites em nos informar se tiveres alguma sugestão.
Espero que este artigo seja útil para aprender uma nova linguagem de programação. Boas hackings!