Zen do Python: Escreva Códigos Pythonicos Mais Limpos e Eficientes

Deseja aprimorar a maneira como você escreve código em Python? Descubra como o Zen do Python pode ser seu guia inicial nessa jornada.

Python é conhecido por sua facilidade de aprendizado. No entanto, criar um código idiomático e “pythonic”, que seja simples de manter, pode representar um desafio, principalmente para aqueles que estão começando na programação. O documento PEP-20 introduziu o “Zen do Python”, um poema de Tim Peters, que destaca a importância de escrever código Python que siga as melhores práticas.

Para visualizar o Zen do Python, basta iniciar um interpretador Python (REPL) e executar o comando:

>>> import this
O Zen do Python, por Tim Peters

Bonito é melhor que feio.
Explícito é melhor que implícito.
Simples é melhor que complexo.
Complexo é melhor que complicado.
Plano é melhor que aninhado.
Esparso é melhor que denso.
A legibilidade conta.
Casos especiais não são especiais o suficiente para quebrar as regras.
Ainda que a praticidade vença a pureza.
Erros nunca devem passar silenciosamente.
A menos que explicitamente silenciados.
Diante da ambiguidade, recuse a tentação de adivinhar.
Deve haver uma – e preferencialmente apenas uma – maneira óbvia de fazer algo.
Embora essa maneira possa não ser óbvia a princípio, a menos que você seja holandês.
Agora é melhor que nunca.
Ainda que nunca seja frequentemente melhor que *agora mesmo*.
Se a implementação é difícil de explicar, é uma má ideia.
Se a implementação é fácil de explicar, pode ser uma boa ideia.
Namespaces são uma ideia genial – vamos usá-los mais!

Como se pode observar, a maioria dos princípios do Zen do Python são autoexplicativos. Algumas máximas devem ser interpretadas em conjunto, enquanto outras parecem contradizer princípios anteriores. Mesmo assim, o Zen do Python oferece uma leitura agradável, estimulante e útil!

Interpretando o Zen do Python

O Zen do Python foi concebido com 20 princípios orientadores para a programação em Python. Contudo, até o momento, existem apenas 19 máximas. Vamos analisá-las:

Bonito é melhor que feio.

Este princípio destaca a importância de escrever código elegante e “pythonic”.

O trecho de código a seguir apresenta um “cheiro de código” (code smell):

def square(num):
    squares = []
    for i in range(num):
        squares.append(i*i)
    return squares

A função:

  • Cria uma lista vazia.
  • Utiliza um loop para adicionar elementos ao final da lista.
  • Retorna a lista resultante.

Apesar de funcionar corretamente, essa abordagem não é “pythonic” e pode ser difícil de manter.

É possível escrever o código de forma mais elegante, utilizando geradores. Veja a versão equivalente da função acima, utilizando geradores:

def square(num):
    for i in range(num):
        yield i*i

Ou, de forma ainda mais concisa, usando uma expressão geradora:

num = ...
squares = (i*i for i in range(num))

Explícito é melhor que implícito.

Ao escrever código, evite que outros desenvolvedores e usuários tenham que adivinhar o comportamento implícito ou padrão do seu código. Seja claro e explícito. Observe o exemplo de importações curinga:

from some_module import * # importação curinga
from some_other_module import *

result = some_function() # de onde veio isso?

Evite ao máximo utilizar importações curinga, pois elas não são claras e são ineficientes. Seja específico ao importar funções e classes de outros módulos:

from some_module import this_function # importação explícita

result = this_function() # agora sabemos.

Simples é melhor que complexo.

Este princípio afirma que devemos manter nosso código o mais simples possível e evitar complexidade desnecessária. Por exemplo, você pode querer inverter uma string e implementar a seguinte solução recursiva:

def reverse_string(my_string):
  if my_string == "":
    return my_string
  else:
    return reverse_string(my_string[1:]) + my_string[:1]

Embora funcione, esta provavelmente é uma solução excessivamente complexa para um problema simples. Há maneiras mais simples e “pythonic” de fazer isso.

Veja a abordagem utilizando o fatiamento de strings:

>>> rev_string = my_string[::-1]
>>> rev_string
'nohtyP'

Ou utilizando métodos e funções nativas:

>>> rev_string = ''.join(reversed(my_string))
>>> rev_string
'nohtyP'

Complexo é melhor do que complicado.

O que este princípio do Zen do Python busca transmitir?

Inverter uma string em Python é uma operação bastante simples. Na prática, porém, podemos precisar de lógicas mais complexas. Veja um exemplo simples:

Imagine que você precise se conectar a um banco de dados:

  • Primeiro, você deve analisar um arquivo de configuração TOML para obter as informações de configuração do banco de dados.
  • O conector do banco de dados deve estar instalado.
  • Em seguida, você pode definir uma função para se conectar ao banco de dados, antecipar erros de conexão, implementar o tratamento de erros e mais.
  • Finalmente, depois de conectado, você poderá consultar o banco de dados.

Embora isso ainda seja relativamente simples, requer uma lógica mais complexa em comparação com a inversão de uma string. Contudo, isso não significa que precise ser complicado. Você pode utilizar as funcionalidades dos módulos nativos de forma eficaz e organizar seu código para que outros desenvolvedores possam ler, entender e contribuir para ele.

Plana é melhor que aninhada.

Uma estrutura plana é mais fácil de analisar e entender do que uma estrutura aninhada. Ao trabalhar em um projeto, você pode ser tentado a isolar funcionalidades criando módulos separados. No entanto, muita granularidade pode ser excessiva.

Dito isso, muitas vezes é necessário ir além de uma estrutura plana. Porém, mesmo que seja necessário o aninhamento, mantenha-o no mínimo.

Veja um exemplo:

from db_info.config.actions.parse.parse_config import parse_toml # muito difícil de analisar!
...

from db_config.parse_config import parse_toml # muito melhor!
...

Esparso é melhor do que denso.

Se você está começando na sua jornada como desenvolvedor, pode ser tentado a abusar de alguns dos recursos da linguagem. List comprehensions, por exemplo, são “pythonic”, mas apenas quando utilizadas quando necessário.

Observe a seguinte list comprehension:

prices_dict = {'melons':40,'apples':70,'berries':55}
items = [(fruit,price) for fruit in prices_dict.keys() if fruit.startswith('m') for price in prices_dict.values() if price < 50]
print(items)
# Saída: [('melons', 40)]

Essa list comprehension é muito densa e difícil de analisar. Nesse caso, utilizar um loop ‘for’ equivalente com condicionais seria mais legível. Em outras palavras, a compreensão é difícil de compreender. 🙂

A legibilidade conta.

Você deve sempre escrever um código legível. Veja algumas maneiras simples de melhorar a legibilidade do seu código:

  • Utilize nomes de variáveis descritivos.
  • Adicione docstrings para funções e classes.
  • Comente o código quando necessário.
  • Adicione type hints (dicas de tipo) para os argumentos e tipos de retorno de funções.

Casos especiais não são especiais o suficiente para quebrar as regras.

Você deve, sempre que possível, seguir as regras da linguagem e as melhores práticas recomendadas.

Mas isso sempre é possível? Não, e por isso temos o próximo princípio.

Ainda que a praticidade vença a pureza.

Este é uma continuação do princípio anterior. Embora seja recomendável seguir as regras da linguagem, em certos casos é perfeitamente normal não seguir alguns dos princípios.

Erros nunca devem passar silenciosamente.

Em Python, erros de tempo de execução são bastante comuns. Como boa prática, você deve sempre tratar os erros e não silenciá-los como uma solução rápida.

Você pode antecipar e implementar o tratamento de erros apropriado para os diferentes tipos de erros:

try:  
    # fazendo isso
except ErrorType1:
    # faça algo
except ErrorType2:
    # faça algo mais
...

Evite exceções simples e genéricas. Versões mais recentes do Python (a partir do Python 3.11) oferecem suporte ao encadeamento de exceções e grupos de exceções para executar uma manipulação de exceções mais sofisticada.

A menos que explicitamente silenciados.

Este princípio segue o anterior. Se o projeto exigir ou permitir que o erro seja silenciado, isso deve ser feito de forma explícita.

Por exemplo, ao conectar-se a um banco de dados, você pode encontrar um OperationalError devido a informações de configuração inválidas. Tente se conectar usando a configuração personalizada. Caso ocorra um OperationalError, utilize a configuração padrão e tente se conectar ao banco de dados novamente.

try:
   # conectando usando configuração personalizada
except OperationalError:
   # conecte usando configuração padrão

Diante da ambiguidade, recuse a tentação de adivinhar.

Este princípio do Zen do Python é autoexplicativo. Em caso de dúvida, não chute. Execute o código e verifique a saída. Então, dependendo se você obtém o comportamento desejado, aprimore a legibilidade ou modifique a lógica conforme necessário.

Veja o seguinte exemplo com uma tupla de booleanos:

>>> True, True == (True, True)
(True, False)
>>> True, (True == (True, True))
(True, False)
>>> (True, True) == (True, True)
True

Deve haver uma – e preferencialmente apenas uma – maneira óbvia de fazer algo.

Para realizar uma determinada tarefa, deve haver uma e apenas uma maneira “pythonic” recomendada de fazê-lo. No entanto, para qualquer problema, podemos ter várias soluções.

Até mesmo no exemplo simples de inversão de string, examinamos uma solução recursiva, fatiamento de string e o método `join()`.

Esta também é uma piada interna devido ao uso inconsistente de travessões. Geralmente utilizamos travessões sem espaços no início e no final. Ou podemos utilizá-los com espaços no início e no final.

Portanto, aqui está o que podemos inferir. O princípio que enfatiza que deve haver uma – e apenas uma – maneira “pythonic” de fazer as coisas pode ser escrito de mais de duas formas.

Embora essa maneira possa não ser óbvia a princípio, a menos que você seja holandês.

Escrito em um tom leve, isso se refere a Guido van Rossum, o criador do Python (que é holandês). A maneira (mais) “pythonic” de realizar uma determinada tarefa – surge naturalmente apenas para os criadores do Python.

Portanto, para os desenvolvedores, é necessário experiência – e aprender com a experiência – para aprimorar o aproveitamento dos recursos da linguagem.

Agora é melhor que nunca.

Assim como outros princípios do Zen do Python, este também pode ser interpretado de várias maneiras diferentes.

Uma interpretação é que, como desenvolvedor, é comum procrastinar para começar a codificar um projeto. Em vez de esperar para planejar cada detalhe do projeto, começar agora é uma ideia melhor.

Outra interpretação possível é: o código que é executado em um número finito de etapas – e termina – geralmente é melhor do que o código com erros e que fica preso em um loop infinito.

Ainda que nunca seja frequentemente melhor que *agora mesmo*.

Este princípio parece contradizer o anterior. Embora seja melhor não procrastinar, devemos refletir sobre o problema e projetar o código de acordo.

Codificar um módulo – sem pensar adequadamente – cheio de “cheiros de código” e antipadrões é uma má ideia, pois tal código será difícil de refatorar e implementar medidas corretivas.

Se a implementação for difícil de explicar, é uma má ideia.

Qualquer lógica, por mais complexa que seja, sempre pode ser implementada de uma maneira simples de explicar e fácil de entender.

Se a implementação for difícil de explicar, provavelmente há alguma complexidade desnecessária. O código pode ser modificado ou refatorado para facilitar o entendimento.

Se a implementação for fácil de explicar, pode ser uma boa ideia.

Este princípio está relacionado ao anterior e também é autoexplicativo. Se a implementação pode ser explicada em termos simples, provavelmente é uma boa ideia.

O código cuja implementação pode ser descrita – em termos simples – provavelmente será legível e fácil de entender, com complexidade mínima.

Namespaces são uma ideia genial — vamos usá-los mais!

Em Python, objetos em um escopo específico podem ser acessados utilizando seus nomes dentro de seu namespace. Por exemplo, você pode criar uma classe e utilizá-la como um modelo para criar instâncias da classe. Agora, as variáveis de instância estarão todas dentro do namespace da instância.

Isso nos permite utilizar objetos com o mesmo nome – sem conflitos – já que estão em namespaces diferentes. No entanto, você só deve utilizá-los quando necessário e garantir que a simplicidade e a legibilidade do código não sejam comprometidas.

Conclusão

Isso é tudo por este tutorial! Espero que este guia tenha ajudado você a entender como o Zen do Python enfatiza o estilo de código e as boas práticas de programação em Python. Quanto mais você programar, melhor você se tornará nisso.

Se você tem interesse em aprender a escrever código conciso e legível, leia este artigo sobre one-liners em Python.