Como o zen do Python pode ajudá-lo a escrever um código melhor

Quer melhorar a escrita de código Python? Veja como o Zen of Python pode ajudá-lo a dar os primeiros passos nessa direção.

Python é super simples de aprender. Mas escrever código idiomático e Pythonic fácil de manter pode ser um desafio, especialmente para programadores iniciantes. O PEP-20 introduziu “The Zen of Python“, um poema de Tim Peters, que descreve a importância de escrever código Python que adere às melhores práticas.

Para ler o Zen of Python, você pode iniciar um Python REPL e executar:

>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

Como visto, a maioria dos aforismos no Zen de Python são auto-explicativos. Alguns aforismos devem ser acoplados ao próximo ao interpretar, enquanto outros contradizem um aforismo anterior. No entanto, o Zen of Python é uma leitura divertida, envolvente e prática!

Interpretando o Zen de Python

O Zen of Python foi proposto para ter 20 princípios orientadores para programação em Python. No entanto, existem apenas 19 aforismos até agora. Vamos passar por cima deles.

Bonito é melhor que feio.

Este aforismo enfatiza a importância de escrever um código elegante e Pythonic.

O trecho de código a seguir tem um cheiro de código:

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

A função:

  • Inicializa uma lista vazia
  • Possui um loop dentro da função que anexa elementos ao final da lista e
  • Finalmente retorna uma lista
  12 software de backup gratuito e de código aberto para manter seus dados seguros

Embora isso seja funcionalmente correto – não é Pythonic – e é difícil de manter.

Você pode escrevê-lo com muito mais elegância usando geradores. Aqui está o equivalente da função geradora da função acima:

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

Ou melhor ainda, você pode ter a seguinte expressão de compreensão do gerador:

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

Explícito é melhor que implícito.

Ao escrever código, não deixe outros desenvolvedores e usuários adivinhando o comportamento implícito ou padrão do código. Seja explícito. Veja o exemplo de importações curinga:

from some_module import * # wildcard import
from some_other_module import *

result = some_function() # where did this come from?

Evite usar importações curinga o máximo possível. Porque não é explícito e ineficiente. Seja específico ao importar funções e classes de outros módulos:

from some_module import this_function # explicit import

result = this_function() # we now know.

Simples é melhor que complexo.

Este aforismo afirma que devemos manter o código simples 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 isso funcione, provavelmente é uma solução superdimensionada para esse problema, visto que existem maneiras mais simples e mais Pythonicas de fazer isso.

Aqui está a abordagem de divisão de strings:

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

E aqui está a abordagem usando métodos e funções integrados:

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

Complexo é melhor do que complicado.

Então, o que este próximo aforismo no Zen de Python transmite?

A reversão de string em Python é uma operação super simples. Na prática, porém, podemos precisar de uma lógica mais complexa. Aqui está um exemplo bastante simples:

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

  • Você deve primeiro analisar um arquivo de configuração toml — para recuperar as informações de configuração do banco de dados.
  • O conector do banco de dados deve ser instalado.
  • Você pode então definir uma função para se conectar ao banco de dados, antecipar erros de conexão, implementar o tratamento de erros e muito mais.
  • Por fim, após conectar-se ao banco de dados, você pode consultá-lo.

Embora isso ainda seja bastante simples, ele precisa de uma lógica mais complexa em comparação com a reversão de string. Mas isso não significa que tenha que ser complicado. Você ainda pode usar a funcionalidade do código de módulos integrados de forma eficaz e organizar seu código para que outros desenvolvedores possam ler, entender e contribuir com 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 ficar tentado a isolar a funcionalidade criando módulos separados. No entanto, muita granularidade pode ser excessiva.

  Como instalar o AutoGPT em minutos

Dito isso, muitas vezes você precisa ir além da estrutura plana. Mas mesmo se você precisar de aninhamento, mantenha-o no mínimo.

Aqui está um exemplo:

from db_info.config.actions.parse.parse_config import parse_toml # too difficult to parse!
...

from db_config.parse_config import parse_toml # much better!
...

Esparso é melhor do que denso.

Se você está apenas começando em sua jornada de desenvolvedor, pode ficar tentado a abusar de alguns dos recursos da linguagem. As compreensões de lista, por exemplo, são Pythonicas – mas apenas quando você as usa onde são necessárias.

Observe a seguinte compreensão:

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)
# Output: [('melons', 40)]

A compreensão da lista é muito densa e difícil de analisar. Nesse caso, usar um loop for equivalente com condicionais será mais legível. Ou seja, a compreensão é difícil de compreender. 🙂

A legibilidade conta.

Você deve sempre escrever um código legível. Aqui estão algumas maneiras simples de melhorar a legibilidade do código:

  • Usando nomes de variáveis ​​descritivas
  • Adicionando docstrings para funções e classes
  • Comentar o código onde necessário
  • Adicionando dicas de tipo para argumentos e tipos de retorno de funções

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

Você deve, tanto quanto possível, seguir as regras do idioma e as melhores práticas recomendadas.

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

Embora a praticidade supere a pureza.

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

Erros nunca devem passar silenciosamente.

Em Python, os erros de tempo de execução são bastante comuns. Como boa prática, você sempre deve lidar com 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:  
    # doing this
except ErrorType1:
    # do something
except ErrorType2:
    # do something else
...

Você deve evitar exceções simples e genéricas. Versões mais recentes do Python (desde o Python 3.11) oferecem suporte ao encadeamento de exceções e grupos de exceção para executar uma manipulação de exceção mais sofisticada.

A menos que explicitamente silenciado.

Isso segue o aforismo anterior. Se o projeto exigir ou permitir que o erro seja silenciado, isso deve ser feito explicitamente.

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

try:
   # connecting using custom config
except OperationalError:
   # connect using default config

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

Este aforismo no Zen de Python é auto-explicativo. Na dúvida, não adivinhe. Mas execute o código e verifique a saída. Então, dependendo se você tem o comportamento desejado, melhore a legibilidade ou modifique a lógica conforme necessário.

Tome o seguinte exemplo simples 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 de preferência apenas uma – maneira óbvia de fazer isso.

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

  6 melhores soluções de imagem de desktop para implantar o sistema operacional

Mesmo no exemplo simples de reversã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 usamos travessões sem espaços iniciais e finais. Ou podemos usá-lo com os espaços iniciais e finais.

Então aqui está o que podemos inferir. O aforismo que enfatiza que deve haver uma – e apenas uma – maneira Pythoniana de fazer as coisas pode ser escrito de mais de duas maneiras.

Embora esse caminho possa não ser óbvio a princípio, a menos que você seja holandês.

Escrito em uma nota leve, isso se refere a Guido Van Rossum, o criador do Python (que é holandês). A maneira (mais) Pythonica de realizar uma determinada tarefa – vem naturalmente apenas para os criadores do Python.

Portanto, para os desenvolvedores, é necessário experiência – e aprender com a experiência – para melhorar o aproveitamento dos recursos do idioma.

Agora é melhor do que nunca.

Assim como alguns outros aforismos no Zen de Python, este também pode ser interpretado de várias maneiras diferentes.

Uma interpretação é que, como desenvolvedor, é bastante comum procrastinar para começar a codificar um projeto. Em vez de esperar para planejar os mínimos detalhes 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 — costuma ser melhor do que o código com erros e que fica preso em um loop infinito.

Embora nunca seja muitas vezes melhor do que agora.

Este aforismo parece contradizer o anterior. Embora seja melhor não procrastinar, ainda assim devemos pensar no 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. Porque tal código é 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 forma 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 acompanhamento.

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

Isso está relacionado ao aforismo anterior e também é autoexplicativo. Se a implementação puder ser explicada em termos simples, provavelmente será uma boa ideia.

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

Namespaces são uma ótima ideia — vamos fazer mais deles!

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

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

Conclusão

Isso é tudo para este tutorial! Espero que este guia tenha ajudado você a entender como o Zen of Python enfatiza o estilo de código e as boas práticas de codificação em Python. Quanto mais você codificar, melhor você ficará nisso.

Se você estiver interessado em aprender como escrever um código conciso e legível, leia este artigo sobre one-liners do Python.