3 de outubro de 2008

O Estilo de Programação PEP8


Introdução
PEP8 é um documento que contém convenções sobre padronização de códigos Python, assim como no PEP7 para a linguagem C. Foi criado à partir do estilo de programação de Guido van Rossum e Barry A. Warsaw.

Esta tradução-resumida (por José Lopes de Oliveira Júnior) conta com as principais características do documento original (na opinião do tradutor/resumista), não sendo uma tradução ao pé da letra. Àqueles que desejarem conhecer o conteúdo na íntegra, sugiro que procurem o original.
Python Enhancement Proposals (Propostas de Melhoramentos Python – ou simplesmente PEPs) são documentos técnicos para a comunidade Python, escritos por motivos diversos. Alguns trazem informações sobre novas versões da linguagem, outros, como o número 8, trazem propostas de estilo de programação etc. Para uma listagem completa de todos o PEPs já lançados, o leitor pode acessar o PEP0, que contém um índice para todos os documentos.

Uma Consistência Tola é o Monstro das Mentes Pequenas
Uma famosa frase de Guido diz que um código é muito mais lido que escrito e, assim como o PEP20 (Zen de Python) cita, a legibilidade conta. Isto justifica a criação deste documento.

A palavra chave é consistência. Mais que apenas consistência no código, este documento visa mostrar ao leitor a importância de consistência no projeto como um todo.

Adquirindo consistência, o leitor aprenderá a ser inconsitente quando nenhuma regra for aplicável. Inconsistente sem prejudicar a consistência do projeto.

Layout de Código
Indentação
Deveriam ser usados 4 espaços por nível de indentação. Para códigos realmente antigos, que não devem sofrer grandes mudanças, pode-se continuar usando tabulações de 8 espaços.

Tabulações ou Espaços?
Prefira os espaços para novos projetos, mas saiba que tabulações também podem ser usadas. Apenas nunca misture os dois métodos.

Comprimento Máximo das Linhas
As linhas deveriam ficar limitadas a 79 caracteres de comprimento. Este número torna o código visível na grande maioria dos editores (mesmo em modo texto), além de permitir que códigos possam ser dispostos lado a lado, sem a necessidade de rolagem horizontal nos mesmos.

Strings de documentação (docstrings) e comentários, que podem constituir longos blocos de texto, deveriam ter 72 caracteres por linha.

class Rectangle(Blob):


def __init__(self, width, height,
    color='black', emphasis=None, highlight=0):
    if width == 0 and height == 0 and \
       color == 'red' and emphasis == 'strong' or \
       highlight > 100:
        raise ValueError("sorry, you lose")

    if width == 0 and height == 0 and (color == 'red' or
                                       emphasis is None):
        raise ValueError("I don't think so -- values are %s, %s" %
                         (width, height))
    Blob.__init__(self, width, height,
                  color, emphasis, highlight)
Exemplo 1. Quebras de linhas.

O exemplo 1 mostra como fazer quebras de linhas de forma legível: uso de parenteses, colchetes e chaves para aproveitar a forma embutida de Python quebrar longas linhas e o uso da contra barra para anular o caractere de quebra de linha. Além disso, o exemplo mostra a indentação correta para linhas quebradas e a quebra de expressões sempre após os operadores.

Linhas em Branco (Blocagem)
Separe o cabeçalho de funções e classes com 2 linhas em branco. Definições de métodos de classes deveriam ser separados de suas implementações por 1 linha em branco. Além disso, linhas em branco podem ser usadas parcamente pelo código para separar grupos de funções relacionadas.

Linhas em branco podem ser omitidas em trechos com comandos de uma única linha, agrupando-os. Dentro de funções, linhas em branco podem ser usadas para indicar blocos lógicos de código (e.g., sempre usar uma linha em branco antes e após estruturas de seleção e iteração).

Ainda poder-se-ia usar o caractere de alimentação de formulário (form feed) como indicação de quebra e página (útil em editores de código que suportam este método). Este caractere pode ser obtido pela combinação Control + L (^L) e é ignorado pelo interpretador Python durante a execução do código.

Codificações
De preferência use ASCII ou UTF-8 para garantir compatibilidade com as mais novas versões do interpretador Python (i.e., versão 3.0) e para tornar o código facilmente lido em qualquer editor de textos.

Recomenda-se que o código seja escrito apenas com caracteres ASCII. Outros caracteres podem ser usados nos comentários, interação com o usuário e nome do autor, mas se o código tiver que ser lido por pessoas de outras nacionalidades, sugere-se que todo ele esteja em inglês (inclusive comentários e interface com o usuário), o que elimina a necessidade de uso de caracteres especiais (exceto pelo nome do autor).

Importações
Importações deveriam ser feitas linha a linha. A excessão fica para:

from subprocess import Popen, PIPE

Importações deveriam ficar agrupadas na seguinte ordem:
  1. Importações da biblioteca padrão.
  2. Importações de módulos de terceiros.
  3. Importações específicas à aplicação.
Uma linha em branco deveria separar cada um dos três blocos e deveriam ser colocadas especificações __all__ após as importações.

Importações relativas intra-pacotes são desencorajadas. Use sempre o caminho absoluto de pacotes para todas as importações. Isto se justifica por portabilidade e legibilidade.

Ao importar um classe de um módulo, pode-se fazer:

from myclass import MyClass
from foo.bar.yourclass import YourClass

Mas se o método acima causar problemas com identificadores duplicados no código, pode-se usar a seguinte alternativa:

import myclass
import foo.bar.yourclass

Lembrando que o uso deste último método implica em chamadas assim: myclass.MyClass e assim: foo.bar.yourclass.YourClass.

Espaços em Branco em Expressões e Declarações
Evite espaços extras nas seguintes condições:
  1. Imediatamente dentro de parênteses, colchetes e chaves.
  2. Imediatamente antes de vírgulas, pontos e vírgulas e dois pontos.
  3. Imediatamente antes da abertura de parênteses que iniciam a lista de argumentos de uma função numa chamada à mesma.
  4. Imediatamente antes da abertura de colchetes que inicia uma indexação ou fatiamento.
  5. Mais de um espaço em volta de uma atribuição ou outro operador, para alinhar com outras linhas.
  6. Sempre cerque operadores binários (e.g., =, !=, is, not, and, +, /) com espaços.
  7. Não use espaços em volta do operador de atribuição quando da indicação de um valor padrão (e.g., dentro da lista de parâmetros de uma função e na cláusula return).
  8. Declarações compostas (várias declaração em uma linha) são geralmente desencorajadas.
  9. Às vezes pode estar tudo bem em colocar o bloco de uma estrutura (if/for/while) na mesma linha de declaração da mesma (quando o bloco é pequeno), mas nunca faça isso para declarações multi-cláusulas e evite linhas longas.

Comentários
Comentários que contradizem o código são piores que a falta deles (a desinformação é pior que a falta de informação). Todos os comentários deveriam se manter atualizados com o código a que se destinam.

Comentários deveriam ser construídos com sentenças completas. Se um comentário é uma frase ou sentença, sua primeira letra deveria ser capitalizada (a menos que seja um identificador que começa com letra minúscula). Nunca altere o caso dos identificadores nos comentários!

Se um comentário é curto, então o fim de período (normalmente um ponto final) pode ser omitido. Blocos de comentários geralmente se constituem de um ou mais parágrafos que são constituídos por sua vez de sentenças completas e cada sentença deveria terminar com um período (e.g., ponto final).

Você deveria usar 2 espaços após um fim de período de setença.

Ao escrever em inglês, o guia de estilo Strunk and White se aplica.

Programadores Python de línguas não inglesas: vocês deveriam comentar seus códigos em inglês a menos que tenham 120% de certeza que eles nunca serão lidos por pessoas que não entendem sua linguagem.

Blocos de Comentários
Blocos de comentários normalmente se aplicam a algum ou todo o código que os sucede e são indentados no mesmo nível que este código. Cada linha de um bloco de comentário inicia com um sustenido (#) e um espaço simples. A menos que isto seja um texto indentado dentro do comentário.

Parágrafos dentro de um bloco de comentário são separados por uma linha contendo um único #.

Comentários de Linha
São comentários que residem na mesma linha da declaração. Deveriam ser usados parcamente, separados da declaração com 2 espaços e deveriam iniciar com um # seguido de um espaço. Normalmente são tão óbvios que poderiam ser eliminados, mas se bem usados podem ser muito úteis.

Sim: x = x + 1 # Compensacao para a borda
Não: x = x + 1 # Incrementa x

Strings de Documentação
Convenções para escrita de boas strings de documentação (a.k.a. docstrings) são imortalizadas no PEP257. Este PEP descreve a utilização das três aspas duplas para criação das docstrings como muito importante, além da trinca de aspas duplas que for fechar a docstring estar sozinha em uma linha, com uma linha em branco o separando do texto de documentação. Veja o exemplo 2.

"""Return a foobang

Optional plotz says to frobnicate the bizbaz first.

"""
Exemplo 2. Uma docstring.

Docstrings de uma linha podem ter suas aspas de fechamento na mesma linha.
Todos os módulos, funções, classes e métodos públicos deveriam possuir docstrings. Já os métodos não públicos não precisam das mesmas, mas neste caso deveria haver, pelo menos, um comentário que descrevesse o seu funcionamento, lembrando que este comentário deveria aparecer após a linha de declaração def.

Escrituração de Versão
Para quem usa Subversion, CVS ou RCS, o seguinte trecho deveria ser incluído após a docstring do módulo, antes de qualquer outro código e separado por uma linha em branco da parte de baixo:

__version__ = "$Revision: 63990 $"
# $Source$

Convenções para Nomes
Nesta sessão serão apresentadas recomendações para nomes de identificadores.

Descrição: Estilos de Nomes
Há muitos tipos de estilos de nomes. A seguir alguns comumente usados:
  1. b (letra minúscula)
  2. B (letra maiúscula)
  3. minusculo
  4. minusculo_com_underscores
  5. MAIUSCULO
  6. MAIUSCULO_COM_UNDERSCORES
  7. PalavrasCapitalizadas (ou CapWords ou CamelCase ou StudlyCaps).
    Nota: ao usar abreviações em CapWords, capitalize todas as letras da abreviação como HTTPServerError.
  8. casoMisto (difere de CapWord pelo caractere inicial em minúsculo).
  9. Palavras_Capitalizadas_Com_Underscores (horrível!)

Ainda há o estilo de se usar um prefixo para identificadores com funções semelhantes, como st_mode, st_size e st_mtime. A biblioteca X11, por exemplo, usa um X nas suas funções públicas. Em Python, este estilo é geralmente desnecessário, pois métodos e atributos são prefixados com o nome do objeto e funções são prefixadas com o nome do módulo.

Em adição, as formas seguintes usando underscores no início ou no final do identificador são reconhecidas (normalmente podem ser combinadas com outros estilos):
  1. _underscore_simples_no_inicio: fraco indicador de uso interno. Por exemplo, from M import * não importará objetos que iniciam com underscore.
  2. underscore_simples_no_final_: usado por convenção para prevenir conflitos com palavras chave da linguagem, por exemplo.
  3. __underscore_duplo_no_inicio: e.g., dentro da classe FooBar, __boo torna-se _FooBar__boo.
  4. __underscore_duplo_no_inicio_e_no_final__: usados para objetos "mágicos" e atributos que residem no espaço de nomes controlado pelo usuário (e.g., __init__, __file__, __main__). Nunca crie tais nomes. Apenas use os existentes, como documentado.

Prescrição: Convenção de Nomes
Nomes para Evitar
Não use os caracteres l (éli minúsculo), O (Oh maiúsculo) e I (Ih maiúsculo) sozinhos como nomes de variáveis, pois podem ser confundidos com outros caracteres em determinadas fontes.

Nomes de Módulos e Pacotes
Deveriam ser curtos e com todas as letras minúsculas. Underscores podem ser usados, mas seu uso é desaconselhado. Estes procedimentos se justificam pelo fato de que nomes de módulos tendem a se tornar nomes de arquivos e alguns sistemas de arquivos têm limitação quanto à quantidade de caracteres e quanto ao uso de caracteres não-ASCII (e.g., FAT).

Quando um módulo escrito em C ou C++ tem uma versão em Python que provê um nível mais alto de abstração, costuma-se nomeá-lo com um underscore no início (e.g., _socket).

Nomes de Classes
A maioria dos programadores usa CapWords para identificadores deste tipo. Classes de uso interno seguem o mesmo padrão, mas iniciam com um underscore.

Excessões
Como excessões deveriam ser classes, o padrão de classes é aplicado aqui. A diferença fica por conta do uso do sufixo Error para este tipo de identificadores.

Variáveis Globais
(Espera-se que estas variáveis tenham uso apenas dentro do módulo)

A convenção para definição destes nomes é o mesmo para as funções.

Módulos que foram desenvolvidos para uso via from M import *, deveriam usar o mecanismo __all__ para prevenir exportações globais ou adotar a convenção antiga de usar o underscore como prefixo para as variáveis não públicas.

Funções
Deveria usar letras minúsculas separadas por underscore, para melhorar a legibilidade.

casoMisto pode ser usado em caso de problemas de compatibilidade.

Argumentos de Funções e Métodos
Sempre deveria ser usado self como primeiro argumento para instanciação de métodos.

Sempre deveria ser usado cls como primeiro argumento de métodos de classe.

Se o argumento de uma função for igual a uma palavra reservada, um underscore deveria ser colocado no seu final. Nada de resumir a palavra. Outra alternativa seria a escolha de um sinônimo para o nome.

Projetando para Herança
Decida-se sempre se métodos e variáveis de instância (atributos) da classe serão públicos ou não. Na dúvida, escolha por não públicos, pois é mais fácil tornar público um método não público do que o contrário. Note que foi usado o termo não público, já que em Python nenhum atributo é realmente privado (pelo menos não sem uma boa dose de trabalho). Ainda existem os atributos que fazem parte da subclasse de uma classe (protegidos), assim é importante que se decida quais atributos são públicos, protegidos e quais são apenas internamente à classe.

Desta forma:
  1. Atributos públicos não deveriam possuir underscores no seu ínicio.
  2. Se o atributo público for igual a uma palavra reservada, adicione um underscore ao seu final.
  3. Para atributos simples, é melhor apenas expô-los, sem métodos de acesso ao mesmo.
  4. Se você deseja criar subclasses para sua classe e você tem atributos que não quer que suas subclasses usem, considere nomeá-los com 2 underscores no seu início.

Recomendações de Programação
  1. O código deveria ser escrito de uma forma que não prejudique outras implementações do interpretador Python (e.g., PyPy, Jython, IronPython, Pyrex, Psyco). Por exemplo: em vez de usar as formas a = a + b e a += b para concatenar strings, é preferível usar a forma ''.join(a,b).
  2. Comparações com singletons como None, deveriam ser sempre feitas com is ou is not. Nunca com operadores de igualdade. Além disso, cuidado ao escrever if x se você quer dizer if x is not None (e.g., ao testar se uma variável que por padrão é igual a None foi configurada para outro valor).
  3. Use excessões baseadas em classes e adicione o sufixo Error ao nome criado. Além disso, sempre documente as classes de excessões com docstrings.
  4. Ao elevar uma excessão, use raise ValueError('mensagem').
  5. Ao capturar excessões mencione a excessão específica (não use formas genéricas). Por exemplo (desconsidere os pontos usados para indentar):
    try:
    ....import platform_specific_module
    except ImportError:
    ....platform_specific_module = None
    Se você quiser capturar todas as excessões, use except Exception:.
  6. Para todas as cláusulas try/except, use o mínimo de código possível para a cláusula try, para não mascarar falhas.
  7. Use métodos de manipulação de strings em vez do módulo string (principalmente por questões de desempenho).
  8. Use ''.startswith() e ''.endswith() em vez do fatiamento de strings, para checar prefixos e sufixos.
  9. Comparações de tipos de objetos deveriam sempre usar a função isinstance() em vez de se comparar os tipos diretamente.
  10. Para sequências (e.g., strings, listas, tuplas), aproveite-se do fato de que sequências vazias são falsas.
  11. Não escreva literais que terminam com muitos espaços em branco.
  12. Não compare valores booleanos com True ou False usando ==.
Concluindo...
O PEP8 cobre muitas dos recursos de programação em Python e por isso é uma ótima referência para programadores que desejam usar um estilo padronizado de programação. As vantagens da padronização de código são impressionantes e este documento em especial ajuda a criar códigos muito legíveis, o que facilita muito a leitura dos mesmos. Por isso é mais do que recomendada por mim, a utilização das orientações aqui expostas.

Dúvidas, críticas, sugestões? Comente!


Leia Também

Nenhum comentário:

Postar um comentário