Do jupyter notebook para código profissional – Parte 2


Introdução

Na parte 1 falamos sobre os primeiros passos para transformar um código feito pensando em criar um protótipo em um código feito pensando em criar um produto. Como mencionado, a maioria do material de ensino foca muito em teoria e modelos e pouco em como formatar o código para que ele possa se tornar robusto e duradouro.

Já falamos sobre o uso de classes e documentação, as classes são úteis para poder usar heranças e para organizar o código e a documentação é importante para podermos dar suporte e expandir nosso código além de poder receber novos desenvolvedores na equipe.

Se classes e documentação são os primeiros passos, a parte mais básica, neste texto vamos cobrir tipos, logs, padrões e performance para deixar o seu código ainda mais bem feito e sofisticado.

4 – Tipos das variáveis

Algo que já mencionei anteriormente quando falei de dicionários e também quando falei sobre docstrings é sobre falar dos tipos das variáveis de input e outputs. Principalmente quando estamos iniciando na programação podemos ignorar a importância sobre o tipo das variáveis, principalmente em uma linguagem como Python que não requer nem que o tipo seja definido ao se criar ou carregar uma variável.

O problema é que conforme o seu código fica maior, mais complexo e o tempo passa fica mais difícil entender o que cada variável é e como utilizá-la. Por exemplo, se uma função ou método aceita como um input um objeto como lista (list-like object) mesmo assim pode causar algum problema mais adiante no seu código se futuramente este mesmo objeto acabar precisar ser um array do numpy ou uma série do pandas e o código não mudar o tipo automaticamente.

Confie em mim, já tive muita dor de cabeça com esse tipo de operação, ter cuidado com o tipo exato necessário pode poupar muito tempo e stress para você e sua equipe.

4.1 – Type hints

Os tipos das váriáveis podem ser especificados tanto nos dicionários quanto nas docstrings quando se listam os inputs e outputs mas outra forma de se especificar os tipos das variáveis é usando type hints.

Os type hints dizem os tipos dos inputs e dos outputs de forma simples e concisa. Diferente de outras linguagens de programação, python não levanta erros se os tipos não forem seguidos, parecido com o que acontece com a docstring.

E assim como no caso de docstrings, caso um usuário use o comando help para ter mais informações sobre uma função ou classe, este comando retorna as informações do type hint dessa forma temos duas formas de colocarmos os tipos das variáveis, ou direto no docstring ou separado em type hints.

4.2 – Usando ifs para garantir os inputs corretos

Como eu mencionei anteriormente os type hints não forçam os tipos das variáveis, eles só ajudam a informar os usuários sobre os tipos. Assim sendo é importante usar os if para garantir que os tipos dos inputs estão corretos e retornar um erro ou mensagem caso não estejam.

Recorde que nas docstrings nós especificamos informações sobre os inputs além dos tipos, por exemplo, não só que um input é um float, mas que seja um float maior que 0. Portanto devemos colocar testes sobre os inputs passados usando os ifs também para garantir que os resultados fazem sentido. A segurança de que todos os inputs passados estão corretos podem evitar muitos problemas com erros inesperados.

5 – Git

Quando estamos trabalhando com notebooks é natural criarmos cópias para poder copiar uma parte do código, checar o que uma certa função está fazendo ou criar uma alternativa para ver se funciona.

O problema é que rapidamente acabamos criando várias cópias do mesmo arquivo e fica difícil até acompanhar qual é a diferença entre elas, qual deveria ser usada, qual é a mais recente, etc.

A solução mais simples é aprender a usar o github. Com ele podemos verificar as modificações feitas entre cada contribuição, podemos criar branches diferentes para cada feature diferente sendo desenvolvida e até ajudar a compartilhar nosso trabalho com colegas de forma simples e atualizada.

Finalmente ele ajuda muito no pipeline de produção já que modificações feitas ao git que sejam aceitas na main branch podem ser automaticamente implementadas na produção podendo atualizar o projeto de forma mais fácil e rápida.

6 – Logs

Uma necessidade que temos é acompanhar o que nosso código está fazendo, principalmente para ter um histórico das ações para identificar problemas quando as coisas dão errado. Quando estamos começando a programar usamo e abusamos do print com esse objetivo mas para fazer um projeto profissional precisamos da biblioteca logging.

Com esta biblioteca podemos criar logs que podem ser printados numa janela de comando e que podem ser gravados num arquivo de texto com essas informações. Além disso essa biblioteca tem customizações úteis sobre o formato e sobre o tipo da informação, por exemplo, info, warning, error e critical.
Assim podemos acompanhar nossa ferramenta em tempo real assim como identificar as causas de erros que podem acontecer no ambiente de produção.

7 – Padrões

Já percebeu como quando você está lendo um texto a formatação em geral é a mesma? Já imaginou se a fonte ficasse mudando, não é estranho? Pois é a mesma coisa para ler um código. Quanto mais padronizado, mais fácil de se acompanhar.

E quando se usa um padrão conhecido mais fácil é de se entender e contribuir seguindo o mesmo padrão. O Pycharm por exemplo já te ajuda a padronizar o código usando o pep8, um estilo de código popular para se usar com o python.

Outro exemplo aonde os benefícios de padrões ficam evidentes é quando comparamos o framework Django em relaçãoao Flask. Como o flask não tem regras predefinidas cada desenvolvedor pode escrever da forma que prefirir já o Django como tem certos padrões fica mais fácil de se achar partes específicas do código e contribuir com a expansão do projeto.

8 – Performance

Depois de implementar todas as sugestões anteriores você estará com um produto que em baixo do capô funciona super bem. Você conseguiu implementar vários benefícios ocultos que muitos usuários e clientes podem nem perceber a não ser que não estivesse lá porém há um possível problema que ainda não mencionamos que é muito perceptível para os usuários que é a questão da performance.

Se sua ferramenta demora muito para rodar muitos usuários podem desistir dela, porém existem muitas maneiras de se melhorar a performance, seja com código melhor, com algoritmos melhores ou a maneira mais simples usando multiprocessamento.

A biblioteca multiprocessing permite adicionar paralelismo ao nosso código de forma simples e eficaz podendo aumentar radicalmente a velocidade do nosso produto. Isto porém vem com o custo de máquinas mais robustas que possam rodar vários processos ao mesmo tempo.

Caso o custo seja um problema talvez tenhamos passado muito rápido pela soluções mais baratas, eu já tive código que levavam horas para rodar com algoritmos ruins e que passaram a rodar em minutos usando métodos melhores. Também já vi processamento de dados que levavam horas passarem a minutos quando o código era otimizado porém essas soluções mais baratas nem sempre são possíveis.

Há ainda as situações em que não há como resolver um problema em um tempo razoável, por exemplo, problemas de otimização massicos com milhares de variáveis e restrições vão levar horas independente da qualidade do código e da quantidade de recursos computacionais. Nesse caso a solução é usar processos assincronos, o python tem algumas bibliotecas que fazem isso e até a multiprocessing pode resolver porém é necessária um conhecimento de programação relativamente alto para usar corretamente.

Ufa! Conseguimos! Poderíamos falar muito mais sobre o assunto mas pelo menos conseguimos dar uma pincelada em vários tópicos que podem ajudar muito na melhoria de um código feito no jupyter e ajudar a resolver vários possíveis problemas que podem aparecer.

Conclusão

Adoraria ouvir dos leitores o que acharam das sugestões e o que eles sugeriram que fosse adicionado aos tópicos.

E lembrando que para quem quiser aprender comigo como implementar essas sugestões além de aprender alguns modelos de otimização de inteiros sugiro se inscreverem no meu curso em parceria com a Comunidade de Estatística do Prof. Thiago Marques.