Mostrando postagens com marcador Algoritmos e Programação. Mostrar todas as postagens
Mostrando postagens com marcador Algoritmos e Programação. Mostrar todas as postagens

25 de fevereiro de 2009

Trocando Valores entre Duas Variáveis sem uma Auxiliar

Como trocar os valores de duas variáveis sem usar uma terceira? Suponha duas variáveis:

pipe = 1984 e less = 2009

O objetivo é esse:

pipe = 2009 e less = 1984



A solução mais comum é com o uso de uma terceira variável, que serve como auxiliar, como mostrado na listagem 1. O problema deste método é que ele necessita de uma terceira variável, que é descartada logo depois. Este método possui as vantagens de ser o mais simples e universal, uma vez que pode ser aplicado a qualquer tipo de variável (numérico, string, objeto criado pelo usuário, vetor etc.), além de contar com o constante aumento na capacidade de memória dos sistemas computacionais, o que incentiva o seu uso. Contudo, muitas pessoas, inclusive eu, procuram uma forma alternativa para fazer este trabalho.

>>> pipe = 1984
>>> less = 2009
>>> aux = pipe  # Iniciando a troca...
>>> pipe = less
>>> less = aux
>>> pipe
2009
>>> less
1984
>>> 
Listagem 1. Forma comum de realizar a troca.

Desde que eu comecei a programar, encontro esse problema e já tinha me convencido que não era possível resolvê-lo sem a terceira variável, mas hoje eu descobri como. Em vez de usar operadores aritméticos, basta usar um operador lógico: o XOR bit a bit. As operações bit a bit, também conhecidas como bitwise, operam com os valores a nível de bits, comparando ou operando cada bit que compõe o valor da variável. A listagem 2 mostra como isto pode ser feito com Python.

>>> pipe = 1984
>>> less = 2009
>>> pipe = pipe ^ less  # Iniciando a troca...
>>> less = pipe ^ less
>>> pipe = pipe ^ less
>>> pipe
2009
>>> less
1984
>>> 
Listagem 2. Usando XOR para resolver o problema.

Para quem não entendeu, a operação XOR (a.k.a., Disjunção Exclusiva  e OU Exclusivo) tem a característica de retornar True, caso os dois componentes da operação sejam diferentes e False em caso contrário. Para entender melhor isso, veja a tabela 1.


Tabela 1. XOR

Sabendo como o XOR trabalha, podemos analisar o que acontece. Para isso, coloque os números em questão em notação binária e realize a operação de XOR com cada um dos bits que o compõem (veja a figura 1). Na primeira operação, 1984 ^ 2009, o resultado é 25. Isto indica que, ao fazer 1984 ^ 25, resultará em 2009 e ao fazer 2009 ^ 25, resultará em 1984. Assim, guarda-se o resultado da primeira operação na primeira variável e opera-se este resultado com a segunda, atribuindo a esta, o valor obtido, o que deixa a segunda variável com o valor da primeira. Então faz-se operação semelhante para a primeira variável, para que ela fique com o resultado da segunda variável.


Figura 1. Raio X da operação com o XOR.

A vantagem de se utilizar o XOR é a economia de memória associada ao custo de processamento de operações bit a bit, que costuma ser pequeno, além de ser uma forma elegante de resolução do problema. Contudo, pesa contra este método, o fato de que ele não pode ser utilizado com todos os tipos de dados existentes. Em Python, por exemplo, não pode-se utilizá-lo com strings e objetos criados pelo usuário, a não ser que este implemente o método __xor__(self, other) para os tipos de objetos não suportados. Mas mesmo com esta possibilidade, pode ser mais interessante que o programador utilize o primeiro método nestes casos. Em todo caso, vale a pena testar a utilização do XOR para realização desta operação na sua linguagem de programação preferida, com os mais diferentes tipos de dados, para testar sua utilização e se descobrir algo novo, comente!




Fonte: iMasters Fóruns

Leia Também
  • Sobrecarga de Operadores em Python – Para manipular melhor os seus objetos.
  • Modulus 11 – Um algoritmo muito usado para validação de documentos nacionais.
  • Algoritmo de Luhn – Entenda o funcionamento e algumas aplicações deste famoso algoritmo para criação e checagem de dígitos de verificação.

2 de janeiro de 2009

Falta de Planejamento


Foi encontrado no bolso de um cadáver, quando se preparava a autópsia, a seguinte carta:

Exmo. Senhor Delegado do Ministério Público: Suicidei-me!

Não culpe ninguém pela minha sorte. Deixei esta vida porque um dia a mais que vivesse, acabaria por morrer louco!

Eu lhe explico, Senhor: Tive a desdita de me casar com uma viúva, a qual tinha uma filha se soubesse isto jamais teria casado. Meu pai, para maior desgraça, era viúvo e quis a fatalidade que ele se enamorasse e casasse com a filha da minha mulher. Resultou daí que a minha mulher se tornou sogra do meu pai. A minha enteada ficou a ser minha mãe e o meu pai, ao mesmo tempo, meu genro!

Após algum tempo, a minha filha pôs no mundo uma criança, que veio a ser meu irmão, porém neto da minha mulher que fiquei a ser avô do meu irmão. Com o decorrer do tempo, a minha mulher pôs também no mundo um menino que, como irmão da minha mãe, era cunhado de meu pai e tio do meu filho, passando a minha mulher a ser nora da própria filha!

Eu, Senhor Delegado, fiquei a ser pai da minha mãe, tornando-me irmão dos meus filhos. A minha mulher ficou a ser minha avó, já que é mãe da minha mãe. Assim, acabei sendo avô de mim mesmo!!! Portanto, antes que a coisa se complicasse mais resolvi acabar com tudo de uma vez.

Que confusão! A primeira vez que eu li este texto, chorei de tanto rir! Outro dia eu estava vendo um CD de backup de alguns anos atrás e encontrei este texto no meio dos arquivos. Ao mesmo tempo que fez eu me lembrar de coisas boas da época, acabei criando uma associação entre este texto e o desenvolvimento de projetos, especificamente, programação.

Quem já fez um programa um pouco maior, com várias funções, variáveis, constantes, classes etc.? Quem já criou um programa com vários arquivos? Quem já trabalhou com ponteiros? Se você já fez alguma ou várias dessas tarefas, sabe como é fácil se confundir no meio de tantas referências. É uma função que chama outra, que tem um ponteiro para outro ponteiro, com uma variável que recebe o valor de outra... Enfim, um caos à primeira vista. Um "mundo" criado pelo desenvolvedor, com a organização que o mesmo promoveu.

Para marinheiros de primeira viagem é fácil se perder no meio do desenvolvimento de um projeto. Em certo ponto bate até um desespero pela sensação de estar perdido – pelo menos foi assim que eu me senti quando comecei e ainda sinto às vezes, confesso. Creio que este exemplo serve para exaltar uma etapa importante do desenvolvimento de qualquer projeto: o planejamento.

Planejar evita muitas dores de cabeça. Um planejamento perfeito evitaria todas elas, mas como ninguém é perfeito, tal façanha é impossível. Contudo, evitar 95% dos problemas e esforços desnecessários já é um alento! Por isso, planeje, planeje, planeje! Seu tempo, seus investimentos e os envolvidos agradecem! ;-)


Leia Também

26 de dezembro de 2008

Sobrecarga de Operadores em Python


A série sobre acesso a atributos foi muito boa para que eu fixasse alguns conceitos de OO (Orientação a Objetos) e aprendesse um pouco mais sobre Python. Com isso, foram apresentados os métodos para acesso a atributos __getattr__() e __setattr__() e os métodos para conversão de objetos complexos, criados pelo usuário, para tipos primitivos (que também são objetos em Python, que fique claro) __str__(), __int__(), __float__(), __oct__(), __hex__(), __complex__() e __long__().

Uma outra característica interessante da linguagem é a possibilidade de sobrecarregar operadores, permitindo que o desenvolvedor crie classes cujos objetos possam ser manipulados de forma mais intuitiva. Dessa forma, no caso do exemplo que eu venho citando, da classe Clock, dados dois objetos c1 e c2 é possível que os mesmos sejam somados ou subtraídos como em c3 = c1 + c2 e c3 = c1 - c2. Sem o uso deste método, o desenvolvedor teria que fazer algo como c3 = c1.add(c2), que é menos intuitivo e mais trabalhoso de se fazer.

Acontece que Python permite a sobrecarga de alguns métodos especiais, responsáveis por realizar estas operações. Um exemplo é o método __add__(), que é responsável por sobrecarregar o operador de soma. Assim, caso a classe Clock possua este método implementado, ao se somar dois objetos Clock, o interpretador executa o método __add__() do objeto da esquerda, passando como parâmetro o objeto da direita. Vamos a um exemplo:
class Clock(object):
    def __init__(self, hour_, minute_, second_):
        self.hour = hour_
        self.minute = minute_
        self.second = second_
    
    def __str__(self):  
        return "%0.2d:%0.2d:%0.2d" % (self.hour, self.minute, self.second)
    
    def __add__(self, other):        
        return Clock((self.hour + other.hour) % 24, 
                     (self.minute + other.minute) % 60, 
                     (self.second + other.second) % 60)
    
    def __sub__(self, other):
        hour = self.hour - other.hour
        minute = self.minute - other.minute
        second = self.second - other.second
        
        if hour < 0:
            hour = 24 + hour
        
        if minute < 0:
            minute = 60 + minute
        
        if second < 0:
            second = 60 + second
        
        return Clock(hour, minute, second)
    
    def __eq__(self, other):
        return (self.hour == other.hour and self.minute == other.minute and
                self.second == other.second)
    
    def __ne__(self, other):
        return not(self == other)
    
    def __gt__(self, other):
        if self.hour > other.hour:
            return True
        
        elif self.hour == other.hour:
            if self.minute > other.minute:
                return True
            
            elif self.minute == other.minute:
                if self.second > other.second:
                    return True
                
                else:
                    return False
             
            else:
                return False
        
        else:
            return False
    
    def __ge__(self, other):
        return (self > other) or (self == other)
    
    def __lt__(self, other):
        return not(self > other) and not(self == other)
    
    def __le__(self, other):
        return (self < other) or (self == other) 

# 
# Main 
# 

c1 = Clock(18, 37, 32)
c2 = Clock(20, 0, 30)

print type(c1)

exit(0)

print "c1 object :", c1
print "c2 object :", c2

print "c1  +  c2 =", c1 + c2
print "c1  -  c2 =", c1 - c2
print "c1  == c2 ?", c1 == c2
print "c1  != c2 ?", c1 != c2
print "c1  >  c2 ?", c1 > c2
print "c1  <  c2 ?", c1 < c2
print "c1  >= c2 ?", c1 >= c2
print "c1  <= c2 ?", c1 <= c2
Listagem 1. Classe Clock implementando sobrecarga de operadores.

Neste exemplo, podemos ver implementados os métodos __add__() (10-13), __sub__() (15-29), __eq__() (31-33), __ne__() (35-36), __gt__() (38-57), __ge__() (59-60), __lt__() (62-63) e __le__() (65-66), que sobrecarregam, respectivamente, os operadores +, -, ==, !=, >, >=, < e <=. Como pode ser observado na implementação de cada um, o trabalho consiste basicamente em tratar cada atributo do objeto (self) e daquele que está sendo operado em conjunto com ele (chamado de other, na implementação). É um recurso bastante simples de se implementar e que provê um resultado excelente. Há muitos outros métodos que podem ser sobrecarregados, que possibilitam criar objetos similares aos do exemplo citado, mais adaptados à linguagem e mais fáceis de manipular. Evidentemente, o programador não precisa implementar estes métodos para todo objeto que criar: apenas quando for necessário. ;-)

 

Série Orientação a Objetos: Acessos a Atributos
Leia Também

23 de dezembro de 2008

Convertendo Objetos Complexos para Tipos Primários em Python


Depois da saga sobre a criação de Getters e Setters em Python, por que não estender as funcionalidades dos nossos objetos? Uma situação que muitos programadores certamente passam é a de ter que transformar um objeto em uma string, inteiro, float etc., seja para imprimir na tela ou para prepará-lo para o banco de dados.

O que se faz normalmente nesta situação é criar um método como *2str, que faz a conversão, retornando uma string, por exemplo. Assim, para a classe Clock, proposta nos textos anteriores, haveria um método como clock2str, que seria chamado como print clock.clock2str(), retornando algo como HH:MM:SS (obviamente, o retorno depende da implementação).

Python oferece uma maneira interessante de realizar esta tarefa. Para isto, basta que o programador implemente, dentro da classe, o método __str__(). Este método não recebe qualquer atributo (exceto pelo self) e deve sempre retornar uma string. Retornar qualquer tipo diferente de string gera um erro em tempo de execução, terminando o programa. A grande vantagem de se utilizar este método é que, para imprimir a representação em string do objeto, basta fazer algo como print clock.

Similar ao __str__(), há o __int__(), que retorna um inteiro que representa o objeto. Neste caso, a conversão pode ser feita com o uso da função embutida, int(). Ao encontrar esta função, o interpretador irá procurar o método __int__() do objeto, executando-o em seguida. Da mesma forma, existem os métodos __float__(), __oct__(), __hex__(), __complex__() e __long__(), que são executados, respectivamente, para as funções float(), oct(), hex(), complex() e long().

Nota: Na versão 3.0, o tipo long foi embutido no int, recebendo o nome deste último. Assim, a função long() foi abolida. Apesar de não ter lido especificamente isso, inferi que o método __long__() também deixou de existir.


Desta forma, podemos converter facilmente os objetos que criamos para um dos tipos primitivos de Python. Esta forma de conversão também possibilita a criação de objetos mais adaptados ao interpretador Python, uma vez que, em vez de fazer a conversão assim: objeto.toint(), pode-se fazê-la assim: int(object). Uma forma padronizada de se converter e que não exige muito esforço.

Como de praxe, segue um código de exemplo, com a classe Clock implementando os métodos citados.
class Clock:
    def __init__(self, hour_, minute_, second_):
        self.hour = hour_
        self.minute = minute_
        self.second = second_
    
    def __getattr__(self, name):
        if name == "hour":
            return self._hour
    
        elif name == "minute":
            return self._minute
    
        elif name == "second":
            return self._second
    
        else:
            raise AttributeError, name
    
    def __setattr__(self, name, value):
        if name == "hour":
            if 0 <= value <= 23:
                self.__dict__["_hour"] = value
    
            else:
                raise ValueError, "Invalid %s value: %s" % (name, value)
    
        elif name == "minute":
            if 0 <= value <= 59:
                self.__dict__["_minute"] = value
    
            else:
                raise ValueError, "Invalid %s value: %s" % (name, value)
    
        elif name == "second":
            if 0 <= value <= 59:
                self.__dict__["_second"] = value
    
            else:
                raise ValueError, "Invalid %s value: %s" % (name, value)
    
        else:
            raise AttributeError, name
    
    
    def __str__(self):
        return "%d:%d:%d" % (self.hour, self.minute, self.second)
    
    def __int__(self):
        return int("%d%d%d" % (self.hour, self.minute, self.second))
    
    def __float__(self):
        return float(int(self))
    
    def __hex__(self):
        return hex(int(self))
    
    def __oct__(self):
        return oct(int(self))
    
    def __complex__(self):
        return complex(int(self))
    
    def __long__(self):
        return long(int(self))

# Main

clock = Clock(22, 48, 28)

print "Object to string.: ", clock
print "Object to int....: ", int(clock)
print "Object to float..: ", float(clock)
print "Object to octal..: ", oct(clock)
print "Object to hex....: ", hex(clock)
print "Object to complex: ", complex(clock)
print "Object to long...: ", long(clock)
Listagem 1. Classe Clock com implementação de conversões para tipos primitivos.


Série Orientação a Objetos: Acessos a Atributos
Leia Também

6 de dezembro de 2008

Python 3000, Python 3.0 e Py3K


Não importa qual dos três nomes você escolha. Todos eles se referem à mais nova versão majoritária (estável) da linguagem Python, disponível para uso desde 3 de dezembro de 2008. A escolha por uma versão majoritária (3.0) se deve à várias mudanças bruscas que foram feitas na sintaxe da linguagem e em sua API (Application Programming Interface), o que quebra alguns elos de compatibilidade com versões anteriores (i.e., série 2.x).

De fato, foram muitas mudanças, tantas que a série de PEPs numerados entre 3000 e 3099 foram designadas apenas para descrever funcionalidades da nova versão da linguagem. Além disso, a documentação do Py3K vem com uma seção especial chamada What's New In Python 3.0, que mostra os aspectos em que a nova versão difere das anteriores. Com base nisso e em pequenos testes que fiz na nova versão, resolvi escrever este texto para mostrar ao leitor alguns pontos importantes de mudança.

1. print tornou-se uma função. Isso mesmo! Se você tentar usar o modo antigo do print no Py3K, vai resultar num erro como o mostrado abaixo:
>>> print "http://versaopropria.blogspot.com"
SyntaxError: invalid syntax (<pyshell#0>, line 1)
>>>

Agora o uso de print deve ser feito como para qualquer função: print(). Particularmente, eu achei muito interessante esta mudança, visto que o comando ficou mais poderoso. Exemplos:
>>> print("http://versaopropria.blogspot.com")
http://versaopropria.blogspot.com
>>>
>>> print("Calculando...", 1 + 1)
Calculando... 2
>>>
>>> print("Evitando a quebra de linha.", end=' '); print("A quebra foi substituída por um espaço!")
Evitando a quebra de linha. A quebra foi substituída por um espaço!
>>>
>>> print()  # Uma linha em branco.

>>>
>>> address = "http://versaopropria.blogspot.com"
>>>
>>> print("Salve", address, "nos seus favoritos! :D")
Salve http://versaopropria.blogspot.com nos seus favoritos! :D
>>>

2. range() se comporta agora como xrange(). O segundo não existe mais! A diferença entre os dois era que, enquanto o primeiro criava uma lista completa a partir dos valores passados, o segundo criava a lista apenas quando os valores eram acessados, o que poupava memória quando a lista não estava em uso.

3. A divisão com uma barra simples retorna float. Use barra dupla para que um inteiro seja retornado.
>>> 1 / 7
0.14285714285714285
>>> 1 // 7
0
>>> 10 / 3
3.3333333333333335
>>> 10 // 3
3
>>>

4. Finalmente UTF-8! Não existe mais a necessidade de se indicar ao interpretador que você vai usar UTF-8. Isso agora é padrão. Só lembre de não abusar! Tenha sempre o PEP8 em mente!

5. as, with, True e False agora são palavras reservadas da linguagem.

6. Agora existe apenas um sinal de diferente: !=. <> foi removido.

7. Uma nova forma de se formatar Strings. Como strings em Python são objetos, foi criado um método chamado format() para substituir o uso do caractere de módulo. Embora na versão 3.0 ainda seja possível uma construção como:
>>> "Meu blog é: %s." % "http://versaopropria.blogspot.com"
Meu blog é: http://versaopropria.blogspot.com.
>>>

Esta forma será abandonada na versão 3.1 da linguagem, o que é um incentivo para que não seja mais usada. O mesmo código poderia ser reescrito com o uso do método format():
>>> "Meu blog é: {0}.".format("http://versaopropria.blogspot.com")
Meu blog é: http://versaopropria.blogspot.com.
>>>

Para quem não entendeu, o novo método funciona basicamente como o anterior, mas em vez de você marcar onde vai ser um float, inteiro ou string, você apenas define onde entrarão o primeiro, segundo e enésimo valores, definidos na própria string, de {0} a {n}. Então você precisa suceder a string com .format() e adicionar, como parâmetros para o método, os valores que deverão ser substituídos na string.

Obviamente esta é apenas a ponta do iceberg, maiores informações podem ser obtidas na página do PEP3101, que é o que define o funcionamento deste novo ambiente.

Pelo pouco que pude perceber da nova versão de Python, a linguagem evoluiu bastante, mesmo que isso tenha custado a perda de compatibilidade com as versões anteriores. Ao meu ver, essa perda de compatibilidade é necessária para que antigas decisões que não se mostraram muito boas, possam ser corrigidas. Minha única crítica fica para o time de desenvolvimento que lançou apenas o instalador para o Windows. Ignoraram Esqueceram os usuários de OS X e Linux! Podiam ter, ao menos, trabalhado em conjunto com a Apple e com a Canonical, para lançar simultaneamente versões para Mac e Ubuntu...

Com o tempo, postarei aqui novas mudanças que eu encontrar. E você? Encontrou alguma mudança para sua forma de programar em Python? Achou as mudanças boas ou ruins? Comente!



Leia Também

22 de novembro de 2008

Manipulação do PATH de Python


Quem já trabalhou com Shell Script ou já fez algum curso mais aprofundado sobre um sistema operacional, sabe da importância da variável PATH. Pythonicamente falando, a variável PATH é uma lista de strings, onde cada elemento é um caminho (path) para um diretório do sistema onde há bibliotecas, executáveis, arquivos de documentação e coisas do tipo. Dessa forma, quando você manda o sistema executar um comando, ele procura em todos os diretórios presentes no PATH, verificando se o mesmo está lá. Se estiver, ele o executa. Senão:



Esta variável é fundamental para o funcionamento dos principais sistemas operacionais da atualidade (Linux, OS X e Windows), tanto que programas como o interpretador Python implementam um sistema similar de busca para seus módulos.

Como este tutorial explica, quando um módulo chamado spam (por exemplo) é importado, o interpretador procura por um arquivo chamado spam.py no diretório corrente (o diretório de onde o interpretador foi chamado) e no PATH de Python, para importá-lo.

Para visualizar o PATH de Python, basta importar o módulo sys e executar o comando sys.path. A listagem abaixo mostra a saída deste comando no OS X.

Python 2.5.1 (r251:54863, Apr 15 2008, 22:57:26)
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path
['', '/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python25.zip', '/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5', '/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/plat-darwin', '/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/plat-mac', '/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/plat-mac/lib-scriptpackages', '/System/Library/Frameworks/Python.framework/Versions/2.5/Extras/lib/python', '/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/lib-tk', '/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/lib-dynload', '/Library/Python/2.5/site-packages', '/System/Library/Frameworks/Python.framework/Versions/2.5/Extras/lib/python/PyObjC']
>>>
Listagem 1. Saída do comando sys.path no OS X Leopard.

No caso, path é uma variável do módulo sys que pode ser manipulada como qualquer lista. Desta forma, se quisermos, por exemplo, adicionar um diretório próprio de módulo, que, digamos, se localize em ~/Programming/Python/modules, basta executar o comando abaixo:

>>> sys.path.append("/Users/zezim/Programming/Python/modules")
Listagem 2. Adicionando um diretório ao PATH de Python.

Ainda há a possibilidade de se adicionar o diretório no início do PATH, para agilizar um pouco a busca:

>>> sys.path.insert(0, "/Users/zezim/Programming/Python/modules")
Listagem 3. Adicionando um diretório no início do PATH de Python.

Todas as modificações apresentadas funcionam apenas para a sessão aberta de Python, ou seja, depois de fechado o interpretador, todas as modificações serão perdidas. Para alterar permanentemente o PATH de Python, no OS X, adicione a seguinte linha no arquivo ~/.bash_profile (se este arquivo não existir, crie-o):

export PYTHONPATH="$PYTHONPATH:/Users/zezim/Programming/Python/modules"

Então force o BASH a ler o arquivo novamente (ou reinicie-o):

$ source ~/.bash_profile

Abra o interpretador Python e o novo diretório já estará no PATH do mesmo. Para se assegurar disto, liste novamente a variável (o diretório adicionado deverá aparecer na segunda posição da lista).

Apesar de ser possível alterar o PATH do interpretador Python, esta não é uma prática recomendada, pois pode colocar em risco a segurança do sistema, desconfigurar o interpretador e mesmo fazer com que os programas criados percam em padronização. O PEP8, inclusive aconselha a utilizar os diretórios disponibilizados pelo próprio interpretador para armazenagem de módulos (no OS X: /Lybrary/Python/<version>/site-packages). Portanto, apenas altere o PATH do interpretador Python se você realmente souber o que está fazendo.

Ainda é interessante ressaltar que, ao criar um módulo, deve-se nomeá-lo de acordo com o PEP8, para facilitar futuras importações:


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).




Leia Também

4 de novembro de 2008

Modulus 11


Modulus 11 (mod11) é um algoritmo para criar um dígito de verificação baseado numa sequência de números pré determinada. Ele é bastante parecido com o Algoritmo de Luhn, com a diferença de que usa pesos para multiplicar cada dígito da sequência. Estes pesos variam de 2 a n, onde n pode variar de aplicação para aplicação.

Diferentemente do algoritmo de Luhn, é difícil encontrar informações precisas sobre o mod11 e suas implementações variam bastante de programador para programador. Uma boa implementação que eu encontrei, foi a da HP:
  1. Tome uma sequência de números.
  2. Começando da última posição, multiplique o item por 2 e adicione o resultado ao somatório, que deve iniciar com 0.
  3. Repita o passo 2 para os outros itens da sequência, lembrando de incrementar em 1 o fator multiplicador (quando este for maior que o valor máximo de multiplicação, deve ser reiniciado com o valor 2).
  4. Divida o somatório por 11 e considere o resto da divisão.
  5. Subtraia de 11, o valor obtido no passo 4 e o resultado será o dígito de verificação.

Apesar de parecer perfeito à primeira vista, o algoritmo apresentado possui duas falhas. Caso o resultado do passo 4 for 0 ou 1, o resultado do passo 5 será 11 ou 10, respectivamente. Isto contraria a idéia do dígito de verificação ser um número entre 0 e 9.

Segundo a implementação da HP, um resultado 10 significa que a sequência de números é inválida para utilização com o algoritmo. Já um resultado 11 implica que a saída do algoritmo é 0. Seria relativamente fácil implementar esta solução, mas isto significaria que muitos números não poderiam ser usados com este algoritmo.

Para quem não sabe, muitos documentos brasileiros utilizam o mod11 para cálculo dos seus dígitos verificadores, como o CPF, CNPJ e Título de Eleitor, além de códigos mais diversos, como o código de procedimentos do SUS (Sistema Único de Saúde). Sendo assim, era preciso adaptar o algoritmo para trabalhar com qualquer saída, tratando-a.

A solução que eu encontrei foi:
  1. Tome uma sequência de números.
  2. Começando da última posição, multiplique o item por 2 e adicione o resultado ao somatório, que deve iniciar com 0.
  3. Repita o passo 2 para os outros itens da sequência, lembrando de incrementar em 1 o fator multiplicador (quando este for maior que o valor máximo de multiplicação, deve ser reiniciado com o valor 2).
  4. Subtraia de 11, o resto da divisão do somatório por 11.
  5. Se o resultado obtido no passo 4 for maior que 9, o dígito verificador será 0. Senão, o dígito verificador será o próprio resultado do passo 4.

Como pode ser percebido, a diferença do segundo, para o primeiro algoritmo apresentado é que, em vez de descartar uma sequência de números cujo dígito verificador dê 10, o segundo retorna o valor 0 para o mesmo. Isto garante que o mesmo funcionará para qualquer sequência de números.

Com a implementação do algoritmo mod11 como uma função separada, fica fácil calcular os dígitos verificadores de quaisquer documentos que o utilizem, tornando os códigos mais limpos e aumentando a reusabilidade de código. Para concluir, segue abaixo a implementação deste algoritmo feita por mim em Python.

# Modulus 11
#
# AUTHOR: Jose Lopes de Oliveira Junior
#             http://versaopropria.blogspot.com
#             jlojunior _at_ gmail _dot_ com
#
# DATE: 2008/10/27
#
# LICENCE: GPLv3 - http://www.gnu.org/licenses/gpl.html
#
# DESCRIPTION: Implements Modulus 11 check digit algorithm
# +as a function. See the docstring for more details.

def mod11(list, max_weight=7):

    """Implements Modulus 11 check digit algorithm.

    It's a variation of the HP's Modulus 11 algorithm.
    Requires the sequence to be calculated in a list of
    integers.
    The HP's Modulus 11 algorithm can be accessed through
    the following link:
    http://docs.hp.com/en/32209-90024/apds02.html

    """

    sum = 0
    weight = 2

    # Iterates through the list from right to left,
    # +multiplying each value for it's weight. If
    # +the weight reaches max_weight, then it is
    # +restarted to 2.
    for item in reversed(list):
        sum += item * weight
        weight += 1

        if weight > max_weight:
            weight = 2

    mod = 11 - sum % 11

    # HP's Modulus 11 algorithm says that a 10
    # +result is invalid and a 11 result is equal
    # +to 0. So, both values are returned as 0.
    if mod > 9:
        return 0

    else:
        return mod
Código 1. Modulus 11 em Python.



Leia Também

24 de outubro de 2008

O Algoritmo de Luhn

O Algoritmo de Luhn foi criado por Hans Peter Luhn (1896-1964), cientista da computação que trabalhou na IBM. O objetivo deste algoritmo é criar um dígito de verificação para uma sequência de números.



O maior uso do algoritmo de Luhn é com cartões de crédito, onde as operadoras geram os n - 1 dígitos iniciais (da esquerda para a direita de quem os lê) e o enésimo digíto é calculado de acordo com os anteriores.

O método para realização do cálculo não é muito difícil:
  1. Tome uma sequência de números inteiros positivos (inclusive o dígito verificador) e a inverta.
  2. Adicione o primeiro número (dígito de verificação) ao somatório geral, que deve iniciar com zero.
  3. Multiplique o segundo número por 2 e execute a operação "noves fora", adicionando o resultado ao somatório.
  4. Repita os passos 2 e 3 até o final da sequência, ou seja, ora adicionando diretamente o número ao somatório, ora multiplicando-o por 2, realizando "noves fora" e adicionando ao somatório.
  5. Ao final, verifique se o somatório é divisível por 10. Se for, o número é válido. Senão, é inválido.

Tudo isso é muito sexy, mas até então conseguimos apenas validar um número. Como podemos gerar um dígito verificador?

Bom, para os que gostam de desafio, aí está um. Se conseguir, poste aqui a sua solução ou um link para a mesma.

Se você não está com tempo ou vontade para fazer isso, eis a minha solução (baseada em várias outras que andei lendo):
  1. Tome uma sequência de inteiros positivos e a inverta.
  2. Multiplique a primeira posição por 2 e faça os "nove fora", adicionando o resultado ao somatório, que deve iniciar com 0.
  3. Adicione a segunda posição ao somatório.
  4. Repita os passos 2 e 3, para, ora multiplicar, fazer os "nove fora" e adicionar o resultado ao somatório, ora adicionar o resultado diretamente.
  5. Se o resultado do somatório for múltiplo de 10, o dígito verificador será 0. Senão, deve-se saber quantos dígitos faltam para completar 10, então o dígito verificador será 10 - (resto da divisão do somatório por 10).

Mas por que aquelas contas no passo 5? Acontece que, como descrito no último passo do algoritmo de Luhn, um número válido deve ter o resultado do somatório igual a um múltiplo de 10. Então, se o resultado do somatório encontrado for múltiplo de 10, este não deve ser alterado. Logo, adicionamos 0. Caso contrário, precisamos fazer com que este resultado seja múltiplo de 10, então adicionamos quanto falta para chegar a 10, como forma de "completar" o número obtido. :D

Eu ia postar o código que fiz em Python, mas vou deixar de lição de casa para os leitores. Porém, para ajudar, eis uma série de números com seus respectivos dígitos verificadores, obtidos a partir da aplicação do algoritmo de Luhn. Assim cada um pode testar sua implementação:

Nota: O comprimento da sequência não importa para o algoritmo.


11111-2
12345-5
1123581321-6
98765432-4
14061970-1

Agora um bônus: este link leva a uma página do PayPal com vários números de cartão de crédito (os mesmos da tabela abaixo), para testes de validação.

Nota: Não se esqueça de ignorar o último dígito dos números, pois ele é o dígito verificador a ser calculado.



Tipo do Cartão de Crédito Número do Cartão
American Express 378282246310005
American Express 371449635398431
American Express Corporate 378734493671000
Australian BankCard 5610591081018250
Diners Club 30569309025904
Diners Club 38520000023237
Discover 6011111111111117
Discover 6011000990139424
JCB 3530111333300000
JCB 3566002020360505
MasterCard 5555555555554444
MasterCard 5105105105105100
Visa 4111111111111111
Visa 4012888888881881
Visa 4222222222222

Para completar os desafios desta postagem, há um erro proposital nela (não compromete o entendimento!). Será que alguém consegue descobrir? Comente! ;)


Leia Também

11 de outubro de 2008

Calculando a Data da Páscoa em Python


Você já parou para pensar de onde vieram certas datas comemorativas do nosso calendário?

Estava pensando sobre isso e cheguei à conclusão de que existem três tipos de datas:
  1. Datas fixas (e.g., Natal, Dia do Trabalho, Independência do Brasil, Dia dos Namorados).
  2. Datas fixas por dia da semana (e.g., Dia dos Pais e das Mães).
  3. Datas móveis (e.g., Páscoa, Carnaval, Corpus Christi).

O primeiro tipo de datas não tem mistério: acontece naquele dia e pronto. O segundo tipo também é traquilo de se entender: pegue um dia da semana de determinado mês e você tem a data – no segundo domingo de agosto é dia dos pais, por exemplo. Já o terceiro tipo é mais intrigante: nem sempre o carnaval é no dia 5 de fevereiro e, às vezes, nem é comemorado no mesmo mês de outros anos.

Foquemo-nos no terceiro tipo de datas. Como saber quando será a sexta-feira da paixão ou o Corpus Christi? Não sei se repararam, mas todas estas comemorações móveis estão relacionadas à cultura cristã e todas as datas se baseiam na data da Páscoa. Por exemplo:
  • Carnaval: 47 dias antes da Páscoa.
  • Ramos: 7 dias antes da Páscoa.
  • Paixão: 2 dias antes da Páscoa.
  • Ascenção: 39 dias após a Páscoa.
  • Pentecostes: 49 dias após a Páscoa.
  • Corpus Christi: 60 dias após a Páscoa.

Posto isso, surge a dúvida: como calcular a data da Páscoa? A resposta é que a Páscoa ocorre no primeiro domingo após a primeira lua cheia após 21 de março (equinócio de primavera no hemisfério norte):
  1. Encontre o dia 21 de março.
  2. Encontre a primeira lua cheia após esta data.
  3. A Páscoa será o primeiro domingo após a data encontrada no passo 2.

Curiosidade: A Igreja assumiu que o equinócio de primavera no hemisfério norte sempre ocorre no dia 21 de março e por isso esta data prevalece no algoritmo. Desta forma, tem-se que as datas extremas para a Páscoa são 22 de março e 25 de abril. Contudo, sabe-se que o equinócio pode acontecer até dois dias antes do dia 21 de março.


Difícil? Nem um pouco comparado ao algoritmo de cálculo desta data, do astrônomo francês Jean Baptiste Joseph Delambre (1749-1822):
  1. a = o resto de (Ano ÷ 19)
  2. b = o inteiro de (Ano ÷ 100)
  3. c = o resto de (Ano ÷ 100)
  4. d = o inteiro de (b ÷ 4)
  5. e = o resto de (b ÷ 4)
  6. f = o inteiro de [(b + 8) ÷ 25]
  7. g = o inteiro de [(b - f + 1) ÷ 3]
  8. h = o resto de [(19 * a + b - d - g + 15) ÷ 30]
  9. i = o inteiro de (c ÷ 4)
  10. j = o resto de (c ÷ 4)
  11. k = o resto de [(32 + 2 * e + 2*i - h - j) ÷ 7]
  12. m = o inteiro de [(a + 11 * h + 22 * k) ÷ 451]
  13. n = o inteiro de [(h + k - 7 * m + 114) ÷ 31]
  14. p = 1 + o resto de [(h + k - 7 * m + 114) ÷ 31]
  15. A Páscoa será no dia p do mês n.

Ainda não está complicado o bastante? Então vamos melhorar as coisas.

O calendário que utilizamos atualmente foi criado pelo Papa Gregório XIII em 1582 e por isso passou a ser denominado Calendário Gregoriano. Antes desta data, era usado o Calendário Juliano, criado pelo então imperador de Roma, Júlio César em 46 a.C. Desta forma, para anos inferiores a 1583 [1], outro algoritmo de cálculo para a Páscoa deve ser usado:
  1. a = o resto de (Ano ÷ 4)
  2. b = o resto de (Ano ÷ 7)
  3. c = o resto de (Ano ÷ 19)
  4. d = o resto de [(19*c + 15) ÷ 30]
  5. e = o resto de [(2 * a + 4 * b - d + 34) ÷ 7]
  6. f = o inteiro de [(d + e + 114) ÷ 31]
  7. g = 1 + o resto de [(d + e + 114) ÷ 31]
  8. A Páscoa será no dia g do mês f.

Sabendo dos algoritmos, basta codificá-los numa linguagem de programação para que o computador possa realizar os cálculos para nós. No link abaixo há um programa completo (desenvolvido por mim) em Python que implementa, além da função para cálculo da data da Páscoa, rotinas que calculam o Carnaval, Domingo de Ramos, Sexta-feira da Paixão, Ascenção de Cristo, Pentecostes e Corpus Christi, de acordo com um ano especificado pelo usuário.

Só para concluir, é interessante notar como este tipo de problema nos enriquece culturalmente quando bem explorados. A dúvida inicial era como os dias são calculados e, para descobrirmos isso, tivemos que aprender, além do algoritmo, um pouco da história da evolução dos calendários. Então, se o tempo levado para entendimento do problema for comparado com o tempo levado para codificação do algoritmo, percebe-se claramente que o primeiro demandou mais tempo que o segundo. Para mim, isso só reforça a tese de que a maior parte do tempo de desenvolvimento de um projeto (software) deve estar focada no seu planejamento (análise e engenharia) e a menor parte na implementação (programação).
Nota: Os algoritmos para cálculo da data da Páscoa e algumas informações sobre esta data foram retirados da página pessoal de Telmo Ghiorzi.


[1] Prefiro usar o ano de 1583, já que o calendário gregoriano começou a ser usado na segunda metade do ano de 1582 – sim, estou arredondando!

Leia Também

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