Python ainda é pythônico?

Eu estava lendo um (longo) artigo que fala sobre várias coisas sobre como desenvolvíamos software no passado e como desenvolvemos hoje, etc… E num determinado momento ele fala sobre bibliotecas, frameworks e sobre um conceito denominado por ele “radius of comprehension“.

O conceito de “radius of comprehension” é simples: dado um determinado trecho pequeno de código, quanto de código a mais você precisa ler para entender o que ele faz. Quanto menor o “radius of comprehension” melhor.

Ao ler isso eu me lembrei de uma palestra do Luciano Ramalho onde ele fala que gosta da linguagem Python (e da idéia de algo ser ‘pythonico’) porque ela “cabe na cabeça” dele e acho que isso tem uma relação direta com a idéia de “radius of comprehension“.

Eu também pensava da mesma forma que o Luciano. Achava que Python “cabia” na minha cabeça. Mas hoje eu devo admitir que isso é parcialmente verdade.

Em primeiro lugar eu acho que a linguagem vem crescendo demais desde a versão 2.2. Adicionando funcionalidades e “acúcares sintáticos” cujos benefícios podem ser facilmente questionados.

Mas não vou falar muito da linguagem não. Vou falar de alguns módulos da biblioteca padrão do Python que são praticamente impossíveis de serem usadas sem ter a documentação à mão:

  • logging – esse é o pior de todos. Não tem uma única vez que eu não preciso recorrer à documentação do módulo pra fazer algo “bobo”. Sei que um “logging.log(…)” basta para o básico, mas se você precisa de algo básico+1 você já se vê obrigado a ler toda a documentação do módulo. Acho que o problema desse módulo foi tentar copiar o log4j do mundo Java. Todos nós conhecemos o gosto por “over engineering” dos programadores Java.
  • ConfigParser – não tenho muito a dizer. Porque esse módulo precisa daqueles conceitos complicados de configurações “globais”, busca de arquivos em múltiplos diretórios, etc. Ele poderia implementar o simples e fácil e deixar as extensões para bibliotecas de terceiros.
  • email – precisei usar esse aqui uma única vez e desde então eu tremo de medo sempre que alguém me pede pra “manipular e-mails” em uma aplicação Python. Sei que manipular e-mails é complicado se considerarmos a quantidade de “variações dos padrões” existentes num serviço que tem quase a idade da Internet, mas as abstrações devem ser construídas para nos poupar desses detalhes, não?
  • xml.dom e xml.sax – eu já não gosto de XML, imagina manipular XML com essas duas bibliotecas “podres”. Se eu pego um código pra dar manutenção e esse código usa uma dessas duas bibliotecas eu triplico o valor do serviço.
  • re – neste caso eu não acho que seja muito culpa do Python, mas peça pra alguém que conheça regex mas não conheça Python explicar a diferença entre re.search() e re.match(). Ou mostre pra ele uma regex com “(?P<foo>.*)” significa.

Além dessas tem outras que não vou me lembrar. Tem também algumas bibliotecas, módulos e frameworks no atual “eco sistema” do Python que simplesmente não entram na minha cabeça:

  • Zope e Plone – a turma do PythonBrasil já conhece essa história. Eu tentei bem umas 3 vezes estudar essa dupla e nas 3 eu não consegui ir além de abrir a interface de administração do Zope. Mas de duas uma: ou sou burro demais ou o troço é desnecessariamente complicado. Por sorte o ZODB funciona separadamente porque esse sim é poderoso e fácil de usar.
  • Buildout – esse eu já usei basicamente pra instalar alguns programas mas é uma caixa preta completa pra mim. Não tenho certeza, mas acho que ele nasceu no (ou em função do) Zope/Plone e talvez isso explique um pouco disso. Eu não sei direito se ele é um sistema de build, um sistema pra criação de ambiente de desenvolvimento, ou se é algum sistema alienígena de IA.

Eu sei que os problemas que temos que resolver hoje em dia são bem mais complexos do que os que tínhamos que resolver quando comecei a programar, mas problemas complexos não precisam de soluções que deixem transparecer essa complexidade.

Textmate “Django test executator tabajara”

O script a seguir pode ser usado para executar testes automatizados em uma aplicação Django a partir do Textmate.

Ele deve ser usado como um “command” no bundle “Python Django” do Textmate e funciona exclusivamente com o sistema de testes do próprio Django (manage.py test).

#!/bin/bash
 
python=$(/usr/bin/which ${TM_PYTHON:-python})
project="$(dirname "$TM_DIRECTORY")"
app="$(basename "$TM_DIRECTORY")"
 
# virtualenv
if [ -x "$project/../bin/python" ]; then
   python="$project/../bin/python"
fi
 
class_filter="s/^ *class +([A-Za-z0-9_]+Test)(.*Test.*):/1/p"
test_filter="s/^ *def +(test[a-zA-Z0-9_]+) *(self.*$/1/p"
 
echo "<html><head>"
echo "<title>Django test results</title>"
echo "</head><body>"
echo "<h1>Django test results</h1>"
if [ -f "${project}/settings.py" -a 
     -f "${project}/manage.py" ]; then
  echo "<h2>Test Results for: $app</h2>"
  echo "<pre>"
 
  # selected text test
  tests=$(
    if [ x"$TM_FILENAME" = x"tests.py" -a 
         "$TM_SELECTED_TEXT" ]; then
      classname=$(
        sed -nE "1,${TM_INPUT_START_LINE}p" "${TM_FILEPATH}" |
        sed -nE "$class_filter" |
        tail -1
      )
      echo "$TM_SELECTED_TEXT" | while read l; do
        class_parse=$(echo "$l" | sed -nE "$class_filter")
        [ "$class_parse" ] && classname="$class_parse"
        test_parse=$(echo "$l" | sed -nE "$test_filter")
        [ "$test_parse" ] && echo "$app.$classname.$test_parse"
      done
    fi
  )
 
  [ -z "$tests" ] && tests="$app"
  $python $project/manage.py test $tests
  # I don't have time to "fight" against wordpress
  # echo "CLOSE_pre_tag"
else
  echo "<p style="color:red;">Error: Django App expected.</p>"
fi
# Again, I don't have time to "fight" against wordpress
# echo "CLOSE_body_tag CLOSE_html_tag""
  • Este comando requer que o Bundle “Python Django” esteja instalado. Existe uma versão no repositório do próprio Textmate e uma versão atualizada no bitbucket.
  • Acione “Bundles->Bundle Editor->Show Bundle Editor”.
  • Selecione “Python Django” e clique no botão “+ New Command”.
  • Escolha um nome para o comando. Aqui eu uso “Run App Tests”.
  • Configure as opções do comando conforme abaixo:
    • Save: All files in Project
    • Commands: O script acima
    • Input: None
    • Output: Show as HTML
    • Activation: Key Equivalent ⌃⌥⌘R
    • Scope Selector: source.python.django

Modo de usar

  • O comando é acionado com as teclas ⌃⌥⌘R.
  • O comando executa apenas os testes da aplicação à qual o arquivo que você está editando pertence.
  • Por padrão o comando executará todos os testes da aplicação.
  • Se você selecionar o os testes que você quer executar no arquivo django_app/tests.py o comando executará apenas esses testes.
  • O comando tem um suporte básico para usuários do virtualenv. Para que o comando use o virtualenv é necessário estruturar os seus diretórios da seguinte maneira: virtualenv_root/django_project/django_app. Isso é necessário para que o comando encontre o interpretador virtualenv_root/bin/python.

NoSQL: Os problemas de um termo infeliz

Foto: Eirik Stavelin

Hoje o meu amigo @epx141 escreveu o seguinte no Twitter:

#NoSQL is a typical confusion between API and backend implementation. SQL indeed sucks, but the storage engines behind it are great.
Instead of writing a new db from scratch, with 20 years of bugfixing ahead, why not take MySQL and make a better query language/API on top?

O termo NoSQL é tão infeliz que levou o @epx141 a compreender de maneira totalmente inversa o que, de fato, é o tal NoSQL.

O que o @epx141 diz que é “great” (storage-engine, modelo de dados, etc) é justamente o que os desenvolvedores desses bancos de dados acham que “sucks”. E o que o @epx141 diz que “sucks” é justamente o que esses desenvolvedores gostariam de ter em seus bancos de dados (uma linguagem universal que explore bem as características de cada banco).

NoSQL é um termo infeliz porque os bancos de dados que fazem parte desse grupo não tem nada contra a linguagem SQL em si. Eles estão justamente ‘contestando’ a hegemonia do *modelo relacional* dos bancos de dados que, em sua imensa maioria, utiliza a linguagem SQL para manipular os seus dados.

Uma definição mais adequada para esse tipo de banco de dados deveria dizer algo similar à “bancos de dados não-relacionais”. Algumas pessoas, no Brasil, estão usando a sigla MRNN, mas não acho que esse termo ‘pegue’.

O termo NoSQL ‘pegou’ porque a linguagem SQL só faz sentido no contexto dos bancos de dados relacionais. Mas ninguém é contra a criação de uma linguagem ‘universal’ para manipulação de dados em bancos de dados não-relacionais.

O CouchDB até mesmo fez isso: usa REST, JS e MapReduce para manipulação dos seus dados. O MongoDB, criou uma variante do SQL. Já para Bancos de dados do tipo chave-valor talvez não faça muito sentido criar uma linguagem para acesso aos dados, afinal, esse tipo de banco de dados não passa de um ‘big dicionário distribuído, redundante, etc’.

O @epx141 é um cara que gosta do modelo relacional para bancos de dados. Ele também acha que eu não gosto. A minha opinião é um pouco mais “cinza”: Eu acho que bancos de dados relacionais tem a sua utilidade mas ela, é muito menor que a utilidade de um banco de dados OO (ZODB, Caché, …) ou um banco de dados de documentos (CouchDB, MongoDB, etc).

É comum eu adotar uma postura ‘radical’ pra expressar essa opinião, mas a razão disso é justamente a de chocar e provocar a reflexão de uma manada de desenvolvedores que usa bancos de dados relacionais “por que sim”.

É óbvio que os bancos de dados não-relacionais ainda não dispõem da maturidade, robustez, etc, etc dos bancos de dados relacionais que existem hoje. É óbvio que ‘ninguém é demitido por usar RDBMS’. Mas não podemos nos esquecer de contextualizar as discussões.

Quando a IBM surgiu com a idéia do modelo relacional (Edgar F. Codd trabalhava para a IBM) os desenvolvedores torceram o nariz da mesma maneira. Os bancos de dados relacionais eram pavorosos e pouco maduros. O ciclo se repete agora.

Esse tipo de reação é freqüente no nosso mundo. É o medo da mudança. O medo do novo. Lembro de vários desenvolvedores Java falando da perfeição da sua linguagem/plataforma que hoje são referências no desenvolvimento Ruby. Tudo isso no tempo em que eu defendia o uso de Python (que, exceto pela sintaxe, é uma linguagem próxima de Ruby).

Precisamos ter em mente que a adoção de ferramentas (r)evolucionárias aumenta o risco dos projetos e que grandes riscos podem trazer grandes retornos (ou grandes prejuízos). O mundo da TI é muito cruel com os reacionários.

Gostou desse artigo?

Assine a minha newsletter quinzenal e receba artigos sobre Programação, Python, Django, carreira e empreendedorismo.

[mc4wp_form]

Home, End, Page Up and Page Down configuration for Mac (Snow Leopard)

Terminal.app

Enter “Terminal->Preferences->Settings->Keyboard“, and change the following options (press <ESC> to get \033 in text below):

Vi Mode

Home: \033[H
End: \033[F  
PageUp: \033[5~  
PageDown: \033[6~

Emacs Mode

Home: \033[1~
End: \033[4~
PageUp: \033[5~
PageDown: \033[6~

Bash/Readline

Edit your ~/.inputrc and add the following lines:

Common settings

# Be 8 bit clean.
set input-meta on
set output-meta on
set convert-meta off

# allow the use of the Delete/Insert keys
"e[3~": delete-char
"e[2~": quoted-insert

# mappings for "page up" and "page down"
# to step to the beginning/end 
# of the history
"e[5~": beginning-of-history
"e[6~": end-of-history

Vim mode settings

# Vi mode
set editing-mode vi
set keymap vi

# allow the use of the Home/End keys
"e[H": beginning-of-line
"e[F": end-of-line

Emacs mode settings

# allow the use of the Home/End keys
"e[1~": beginning-of-line
"e[4~": end-of-line

Vim

Edit the file ~/.vimrc and add the following options:

Terminal configured with Vi mode

map <Esc>[H <Home>
imap <Esc>[H <Home>
map <Esc>[F <End>
imap <Esc>[F <End>
map <Esc>[5~ <PageUp>
imap <Esc>[5~ <PageUp>
map <Esc>[6~ <PageDown>
imap <Esc>[6~ <PageDown>

Terminal configured with Emacs mode

map <Esc>[1~ <Home>
imap <Esc>[1~ <Home>
map <Esc>[4~ <End>
imap <Esc>[4~ <End>
map <Esc>[5~ <PageUp>
imap <Esc>[5~ <PageUp>
map <Esc>[6~ <PageDown>
imap <Esc>[6~ <PageDown>

Cocoa Keybindings

Put the following lines in ~/Library/KeyBindings/DefaultKeyBinding.dict:

{
    /* home */
    "UF729" = "moveToBeginningOfLine:";
    "$UF729" = "moveToBeginningOfLineAndModifySelection:";

    /* end */
    "UF72B"  = "moveToEndOfLine:";
    "$UF72B" = "moveToEndOfLineAndModifySelection:";

    /* page up */
    "UF72C"  = "pageUp:";
    "$UF72C" = "pageUpAndModifySelection:";

    /* page down */
    "UF72D"  = "pageDown:";
    "$UF72D" = "pageDownAndModifySelection:";
}

Firefox

Install the following Firefox extension.

Textmate

I strongly recommend to Textmate users to disable the “Smooth Scrolling” option in System Preferences->Appearance->Use smooth scrolling. This option cause a strange behaviour with Page Up/Down on Textmate.

References

Update: Added suggestion to disable “Smooth Scrolling” for Textmate users.

É mais fácil pedir desculpas do que permissão

Diferente do que escrevi no post Dicas para um bom programa em Python, onde eu dou dicas de como proceder para ter um programa Python melhor, desta vez vou falar sobre um estilo que prefiro. Não quero dizer que estou certo ou errado, apenas que prefiro assim.

It’s easier to ask forgiveness than it is to get permission — Grace Hopper

Recentemente, dentro do tempo que me sobrava, comecei a desenvolver uma biblioteca pra fazer requisições HTTP para uma API REST. Essa biblioteca seria usada para criar testes automatizados do projeto que iremos começar a desenvolver aqui na empresa.

Essa biblioteca faria basicamente o mesmo que a httplib e httplib2 do Python mas com algumas conveniências: (de)serialização de JSON/XML, conteúdo calculado no momento da request (ex. assinatura da requisição), e uma classe “TestCase-like” com funções que auxiliassem no desenvolvimento de testes.

Eu tinha só algumas idéias do que essa biblioteca faria e quase nada de código quando vi o lançamento do Bolacha, desenvolvido pelo Gabriel Falcão, no Planeta Globo.com. Guardei o link pra conferir depois pois poderia ser útil para o que eu queria fazer.

Ontem eu tive um tempo para analisar e vi que ele não só seria útil como já fazia a parte mais essencial do que eu precisava (requisições GET/POST/PUT/DELETE/…).

Como o projeto esta hospedado no github.com tratei logo de fazer um fork para criar as outras funcionalidades que eu precisava. Código legal, código simples, código bem feito, mas… quando encontrei os primeiros…

def request(self, url, method, body=None, headers=None):
  if not isinstance(url, basestring):
    raise TypeError, 'Bolacha.request, parameter url must be ' 
              'a string. Got %s' % repr(url)
 
  if not isinstance(method, basestring):
    raise TypeError, 'Bolacha.request, parameter method must be ' 
              'a string. Got %s' % repr(method)
 
  if method not in HTTP_METHODS:
    raise TypeError, 'Bolacha.request, parameter method must be ' 
              'a valid HTTP method. Got %s. %s' % (method,
                                                    RFC_LOCATION)
    # ...continua

… notei que o estilo do Gabriel divergia do meu. Nada errado com isso. Tanto que, mesmo assim, continuarei a usar e melhorar o Bolacha mantendo (dentro do possível) o mesmo estilo original do autor para que ele possa aceitar minhas contribuições.

O que não gosto desse estilo é que, com ele, sempre estamos pedindo permissão, ou seja, verificando de antemão alguma informação no lugar de usá-la imediatamente e, só em caso de erro, considerá-las inválida. Nesse caso estamos adicionando um overhead desnecessário (muito pequeno neste exemplo) até mesmo para casos de uso correto do método.

Outro problema que temos nesse estilo reside no fato de que estamos usando o mecanismo de herança da linguagem como um sistema de criação de interface para o objeto. Se eu quiser passar um objeto que se comporta exatamente como uma string mas que não seja uma classe derivada de basestring() para o método .request() acima eu não vou poder.

Eu removeria todas as verificações de isinstance() e deixaria o código assim:

def request(self, url, method, body=None, headers=None):
  if method not in HTTP_METHODS:
    raise TypeError, 'Bolacha.request, parameter method must be ' 
          'a valid HTTP method. Got %s. %s' % (method,
                                                    RFC_LOCATION)
    # ... continua

Mais adiante nesse código vemos o uso de uma função chamada is_file() que é implementada da seguinte forma:

def is_file(obj):
  return hasattr(obj, 'read') and callable(obj.read)

Mais uma vez, nada de errado. Mas também não é muito o meu estilo. No meu estilo essa função sequer existiria porque, mais adiante, quando fosse necessário usar obj que, no código em questão, pode ser uma string ou um file object, eu faria algo assim:

try:
  lines.extend(encode_file(obj))
except AttributeError:
  lines.extend([ '...' ])

Mais uma vez eu quero deixar claro que é só uma questão de diferença de estilo e que eu usei o código do Bolacha somente para ilustrar essa diferença. Dentro do estilo do Gabriel o código está perfeito (tá, não existe código perfeito, mas o dele tá muito bom).

Como leitura complementar sobre essas diferenças eu recomendo o artigo isinstance() considered harmful, Duck Typing, Permission and Forgiveness e PEP-3119 – Introducing Abstract Base Classes (funcionalidade de suporte ao estilo usado no Bolacha).

Por último, ao meu estilo, gostaria de pedir desculpas ao Gabriel Falcão por ter usado o código do Bolacha para ilustrar esse artigo sem permissão. 🙂

Dicas para um bom programa em Python

Foto: Olivier H

Oi pessoal, desta vez eu vou pular as ‘desculpas’ por ter demorado tanto para postar aqui no blog e vamos direto ao assunto.

Recentemente eu tenho trabalhado bastante com Python (dã!) desenvolvendo projetos de diversos tipos e resolvi escrever aqui sobre algumas coisas que pratico enquanto desenvolvo.

Esse artigo é uma espécie resumo com boas práticas de programação Python que utilizo no meu dia-a-dia.

Código mais robusto

Deu certo ou errado?

O que você faz quando acontece algo errado na execução do seu método? O que você responde à requisição que lhe foi feita?

Eu tenho visto em muito código por aí os desenvolvedores retornando valores sentinela (None, null, 0, -1, etc.) para avisar que algo incorreto aconteceu na execução do método.

def f(arg):
   if not arg:
      return None
   return [ "resultado", "de", "um", "processamento"  ]

Algumas linguagens de programação não possuem estruturas de tratamento de exceção e, neste caso, o uso de sentinelas é válido. Mas quando a linguagem de programação te disponibiliza essa funcionalidade é bom usá-la.

def f(arg):
   if not arg:
      raise ValueError("Argumento Invalido")
   return [ "resultado", "de", "um", "processamento"  ]

Deixem as exceções fluirem.

Isso mesmo. A menos que você saiba exatamente o que você deve fazer quando uma exceção aparece deixe-a exceção “subir”. Pode ser que “lá em cima” alguém saiba cuidar dela adequadamente.

Quando não fazemos isso estamos ocultando informação importante para os usuários do nosso código (sejam eles usuários, outros desenvolvedores ou nós mesmos).

def f():
   try:
      return conecta()
   except ExcecaoQueDeveriaSerErro:
      return None

Quando eu implemento esse tipo de método/função eu faço assim (na verdade eu não implementaria f() e chamaria conecta()):

def f():
   return conecta()

O que seu método/função retorna?

Código que eu encontrei recentemente:

def get_fulanos():
   q = Q("select * from patuleia where alcunha like 'fulano%'")
   ret = [ str(fulano['nome']) for fulano in q ]
   if len(ret) == 1:
      return ret[0]
   return ret

Perceberam o que está errado? O seu método retorna uma lista de Fulanos ou retorna Fulano?

Isso está conceitualmente errado e pode fazer você perder horas preciosas do seu dia tentando achar um bug causado por esse tipo de código.

Aconteceu comigo. Note que str() implementa uma interface de sequence da mesma forma que list(). Então o erro passa silenciosamente no caso abaixo:

old_fulanos = [ "Ze Ruela", "Ze Mane" ]
old_fulanos.extend(get_fulanos())
print old_fulanos

Rodando esse código você vai obter ['Ze Ruela', 'Ze Mane', 'q', 'u', 'a', 'c', 'k'] sendo que, em mais de 90% dos casos, o que você gostaria de ter seria: ['Ze Ruela', 'Ze Mane', 'quack'].

“Nada” é diferente de “alguma coisa”.

Essa dica é só uma complementação da primeira e da segunda dica.

Quando o seu método/função retorna uma collection (seqüência, conjunto, hash, etc) vazia você deve retorná-la vazia e não um valor sentinela (como None). Isso facilita a vida de quem vai usar o seu método/função:

def vazio():
   return []
 
for elemento in vazio():
   pass #... faz algo se o conjunto contiver dados ...

Se você retorna um valor sentinela:

def vazio():
   return None
 
elementos = vazio()
if elementos:
   for elemento in elementos:
      pass # ...

Notou que tivemos que criar uma variável com o resultado da operação (para não precisar chamá-la duas vezes) e tratar a sentinela com um “if“? Se eu esqueço de tratar a sentinela meu programa vai quebrar.

Lembre-se sempre que uma collection vazia tem valor booleano “False“.

Todo ‘elif‘ tem um irmão ‘else‘.

Sempre que você precisar usar uma construção if/elif coloque uma cláusula ‘else‘.

Além de usar a cláusula ‘else‘ eu geralmente faço com que ela gere uma exceção. Desta forma eu sou obrigado a trabalhar todas as possibilidades nos ‘if/elif‘ evitando ocultar uma situação que pode ser inválida.

class InvalidCommand(Exception):
   pass
 
def minihelp(comando):
   if comando == "print":
      return u"""Imprime dados na tela.
                 Deixará de ser comando no Python 3.0"""
   elif comando == "assert":
      return u"""Certifica se uma condição é
               verdadeira e gera uma excessão em caso contrário"""
   elif comando == "...":
      pass # ...
   else:
      raise InvalidCommand(u"Comando %s inválido." % (comando,))

Eu gosto dessa prática mas isso não significa que você deva seguí-la sempre. Existem situações onde ter um “valor default” é necessário e nestes casos o uso do else sem levantar exceção se faz necessário.

if comando == "if":
   print "Vai usar elif?"
elif comando == "elif":
   print "Muito bem. Agora falta o else"
else:
   print "Pronto. Agora está bom."

“Pythonismos”

Use mais atributos públicos do que atributos protegidos (“_“).

Programadores acostumados com Java utilizam muito as cláusulas ‘private‘ e ‘protected‘ para encapsular os atributos de um objeto para logo depois implementarem os getters e setters para acessar esses atributos.

Essa prática é aconselhada em Java porque em algum momento do futuro você, talvez, precise validar esses dados ou retornar valores calculados. Nestes casos os programadores apenas implementam essa lógica nos métodos que acessam o atributo privado.

Mas em Python isso não é necessário. Em Python você pode transformar seu atributo público em uma “property” que não muda a forma de se acessar o atributo e permite o acrescimo de lógica ao acesso do mesmo.

Evite usar “__“.

Por convenção, em Python, todo método ou atributo iniciado com um “_” é considerado privado (equivalente ao protected em Java) e não deve ser acessado de fora da classe mesmo sendo possível fazê-lo.

Dito isso parece meio óbvio que não precisamos usar “__” para dificultar ainda mais o acesso à esse atributo/método. Além disso o uso do “__” traz alguns incoveninentes para quem quer derivar a sua classe e acessar este método/atributo já que o Python o renomeia acrescentando o nome da classe ao seu nome (__attr vira _Classe__attr).

Não sobrescreva builtins.

Python disponibiliza várias funções e classes builtins que facilitam muito o uso da linguagem e economizam digitação. Mas esses builtins tem nomes muito “comuns” e frequentemente a gente usa os nomes dos builtins como nomes de identificadores. Eu mesmo vivo (vivia) fazendo isso.

O problema é que em certos momentos alguns problemas podem acontecer quando você tenta chamar um buitin que já não é mais um builtin. Na maioria das vezes o problema “explode” logo e você rapidamente conserta mas em alguns casos você pode perder muitas horas tentando achá-lo.

Algumas pessoas usam um “_” no fim do nome do identificador (ex. “id” vira “id_”) mas eu acho isso um pouco feio então uso só quando não encontro uma alternativa melhor.

Vou colocar aqui uma tabela de equivalências que eu costumo usar para substituir o nome dos builtins mais comumente sobrescritos:

  • id – ident, key
  • type – kind, format
  • object – obj
  • list – plural (lista de element vira elements)
  • file – fd, file_handler
  • dict – dic, hashmap
  • str – text, msg

Análise estática economiza seu tempo.

Eu uso o pylint, mas conheço algumas pessoas que preferem o pyflakes ou o PyChecker.

A dica é essa: usar um programinha de análise estática como esses pode diminuir consideravelmente aqueles errinhos chatos de sintaxe, ou de digitação. Pode limpar os ‘import’ desnecessários do seu software, etc, etc.

É lógico que esse tipo de ferramenta não substitui uma boa política de testes mas é um bom complemento para ela.

Challenge yourself

Máximo de 3 níveis de indentação. (ou 4 se estiver dentro de uma classe)

Ao se esforçar para que seu código não fique muito aninhado você está trabalhando melhor a implementação dos seus métodos e funções. Nem sempre é possível (ou aconselhável) restringir tanto o nível de identação do seu código mas muitas vezes isso melhora a sua implementação.

Máximo de 2 indireções.

Recebeu um objeto como parâmetro? Chame apenas métodos dele e evite ao máximo chamar métodos do retorno desses objetos:

def f(obj):
    obj.metodo() # legal!
    obj.metodo().outro_metodo() # ruim!

Quando você chama um método pra um objeto retornado por outro método você está aumentando o acoplamento entre as classes envolvidas impedindo que uma delas seja substituída (ou reimplementada) ‘impunemente’.

Essa regrinha é uma das regrinhas da Lei de Demeter.

Máximo de 0 ‘if/elif/else‘s.

Polimorfismo é isso. No mundo OO ideal, perfeito e utópico praticamente não precisaríamos do comando “if” e usaríamos somente polimorfismo. Mas… como não conseguimos isso tão facilmente* devemos, ao menos, usar o “if” com moderação.

Conclusão

Esta é uma lista incompleta de dicas para programadores Python. Se futuramente eu lembrar ou aprender algo novo eu volto aqui para falar sobre o assunto.

Alguns desenvolvedores podem não concordar com as dicas. Neste caso eles podem enriquecer ainda mais esse texto argumentando sobre o as suas restrições no espaço para comentários.

Se você tiver alguma dica para compartilhar com a gente coloque nos comentários. Se ela for boa mesmo eu coloco ela no corpo principal do blog.

* eu mesmo só consegui fazer uma aplicação OO funcional sem usar um único if. Era uma implementação do joguinho de adivinhação de animais (aquele que pergunta “Vive na água? (s/n)”) em Smalltalk.

Update: pequena correção sugerida pelo Francisco nos comentários: s/Classe/_Classe/.

Quer mais?

Você pode encontrar várias dicas para desenvolvimento Python (e Django) nos artigos reunidos em minha newsletter quinzenal “O Melhor da Internet”. Você vai receber links com o melhor conteúdo relacionado a Python, Django, Carreira, Negócios e Empreendedorismo.

Para assinar basta preencher o formulário abaixo.

[mc4wp_form]

Latinoware 2008, eu fui…

Como sempre faço quando vou à um evento vou relatar aqui a minha primeira experiência participando da Latinoware 2008.

A decisão sobre a minha participação só foi tomada dois dias antes do evento pois quem iria patrocinar a minha viagem (INdT) conseguiu viabilizar o treinamento de Python para Maemo bem em cima da hora. Participei apenas do primeiro dia do evento.

Cheguei em Foz do Iguaçu na quinta-feira (30/10/2008) de madrugada e tinha um serviço de transfer no aeroporto para carregar os palestrantes até os hotéis onde ficariam hospedados. O meu nome não constava na lista mas isso era justificável por minha decisão tardia. De qualquer maneira bastou uma conversa rápida com o responsável pelo transporte e eu já estava dentro da Van em direção ao hotel.

Quando cheguei no hotel avistei uma placa avisando que um ônibus passaria lá às 8hs da manhã em direção à Latinoware. Às 7h30 eu estava na frente do hotel juntamente com mais 3 outras pessoas que também iriam para o evento.

Desistimos de esperar às 8h30 e dividimos um taxi até o local do evento. Descobri depois que o ônibus sequer havia passado no hotel.

Ao chegar no posto de credenciamento que estava instalado num estacionamento de Itaipu avistamos as filas para pegar crachás. Me dirigi à fila de crachás para “Palestrantes, Imprensa e Autoridades”.

A fila estava pequena (umas 8 pessoas) mas não andou 1 milímetro sequer até que um dos organizadores nos retirou da fila para entregar os crachás que estavam com ele. Mas o meu crachá, afinal, não estava com ele e tive que me contentar com um crachá em branco onde preenchi à mão o meu nome e RG.

Os palestrantes então deveriam entrar num ônibus que estava estacionado ao nosso lado pois seriamos levados até o “Parque Tecnológico de Itaipu” onde a estrutura do evento estava montada.

Esperamos por mais alguns minutos (que pareceram horas) até que um grande amigo meu entra pela porta do ônibus e comenta algo que eu já havia notado: “Esse é o evento mais desorganizado dentre todos os de informática dos quais já participei.” Note que em meu caso e no dele isso significa muitos eventos.

O ônibus saiu com lotação total de pessoas sentadas e em pé. Detalhe: faltavam poucos minutos para as atividades do evento começarem e uma parte considerável dos palestrantes ainda estava em um ônibus saindo da área de credenciamento.

Poucos metros dali o ônibus para e todos que estavam de pé tiveram que trocar de ônibus. A troca foi feita sob a vigilância atenta de um dos seguranças de Itaipu que olhava atentamente para nossos crachás preenchidos à mão para ver se não detectava alguma fraude 😀 Eita segurança…

Finalmente nos salões onde as atividades do evento iriam ocorrer tratei logo de ir para a sala de palestrantes ou para o stand da Associação Python Brasil para me conectar à Internet e terminar o material do meu treinamento…

Vencida a falta de sinalização do lugar fui até a sala de palestrantes mas ela já estava completamente lotada.

Fui então à caça do stand da APyB e ao chegar lá descobri que o ponto de rede estava com problemas. A sorte foi que o Ramiro levou um roteador wireless para conectar no ponto de rede do pessoal do Debian Brasil.

O almoço era razoável e foi ‘di grátis’. Servido apenas até as 14hs fez com que os instrutores dos mini-cursos da manhã tivessem que improvisar suas refeições já que seus treinamentos eram de 4hs (sem intervalo) e terminariam somente às 15hs.

No meu treinamento deu quase tudo certo. Mas nesse caso específico a culpa pelas falahas foram majoritariamente minhas (o ambiente necessário para o treinemento não estava instalado nas máquinas do laboratório pois eu não havia feito a solicitação em tempo hábil). Faltou um flip-chart/quadro branco na sala de aula também.

Terminado o treinamento fui dar uma bisbilhotada no pessoal da Robótica Livre que estava montando plaquinhas Severino sob a orientação do Prof. Paulo Gonçalves. O Severino é uma implementação brasileira do Arduino. Queria ter ficado lá mas tive que ir para a palestra de abertura e depois para um coquetel no mirante de Itaipu onde veríamos o acendimento das luzes da barragem. Um show que eu recomendo. Itaipu é uma daquelas coisas que temos pra mostrar as virtudes do nosso povo.

Aquele quadrado no centro é um prédio de 6 andares
Aquele quadrado no centro é um prédio de 6 andares

Já o coquetel… Eu comi dois “canapés” minguados e não bebi nada (nem água). E os canapés foram doados pelo Ramiro que se comoveu com a minha fome 🙂

Depois do coquetel voltamos para o hotel onde eu dormi um pouco até ir para o aeroporto embarcar de volta para Curitiba onde iria preparar minha ida para São Paulo (desta vez de carro) GP Brasil de F1. Sobre isso eu conto no próximo post.

"Cagadas" homéricas (ou YA-meme?)

Durante toda a minha carreira “computeira” eu cometi alguns erros absurdos (cagadas?) que eu gostaria de compartilhar com vocês neste post.

Vou listá-las aqui na esperança de que outros façam o mesmo e que, com isso, o meu sentimento de culpa por tamanhas “obras” fique menor.

killall foobar

Certo dia, quando trabalhava na GVT, eu estava numa sessão telnet em um servidor de produção PA-RISC que rodava um HP-UX. Estava logado como root e precisava matar alguns processos que estavam prejudicando o funcionamento da máquina (consumindo CPU, memória, load alto, etc).

Meu conhecimento de Unix até aquele momento era de ter mexido muito com Linux ao ponto de saber usar com maestria o comando killall. Foi então o que eu usei para matar tais processos.

Assim que eu apertei “enter” surgiu uma mensagem de que o servidor iria se desligar em X segundos. Entrei em pânico.

Pois bem… apesar de ser um ‘fera de killall’ eu nunca tinha lido o trecho da manpage deste comando que dizia (no Linux):

Be warned that typing killall name may not have
the desired effect on non-Linux systems, especially
when done by a privileged user.

O que aconteceu? O killall do HP-UX envia um sinal para todos os processos da máquina e é usado pelo comando shutdown para matar todos os processos em execução.

Foi assim que eu desliguei, pela primeira vez, um servidor em produção de uma grande empresa de telefonia praticamente parei as vendas da empresa por pelo menos 1 hora (uma máquina dessas leva cerca de 20 minutos para voltar pro ar + acertos de configuração perdidos por causa do reboot).

delete from tabelao;

Mais uma vez na GVT… coitados…

Eu estava desenvolvendo um programinha que fazia acesso ao banco de dados Oracle e para isso eu constantemente testava algumas queries, inserts e deletes nas tabelas de um banco de dados que ficava no ambiente de desenvolvimento da empresa.

Nos bancos de dados do ambiente de desenvolvimento a gente encontrava um subconjunto dos dados do ambiente de produção e podíamos manipulá-los com tranquilidade porque uma vez por dia/semana as bases eram repopuladas com dados oriundos do ambiente de produção.

Em certo momento eu precisava deletar todos os registros de uma tabela (cerca de 50 registros) da base de dados do ambiente de desenvolvimento e rodei um:

delete from tabela;

Fiz os testes que precisava fazer sem rodar um commit e no final fiz um rollback.

Neste intervalo de tempo ocorreu um problema que exigiu minha atenção em um dos servidores de produção e esse servidor era justamente o servidor ‘equivalente’ ao de desenvolvimento onde eu estava trabalhando. Resolvi o problema do servidor rapidamente e voltei para o desenvolvimento.

Aí é que está o problema… o meu cérebro trocou de servidor mas meus dedos não e no fim eu rodei um delete from tabela; no servidor de produção da empresa 😀

O estrago teria sido pequeno se tal tabela não tivesse centenas de milhares de registros fazendo com que o Oracle praticamente parasse de responder à requisições até que ocorresse uma falha de “estouro de segmento de rollback” (falha que demorou pra acontecer porque as configurações do Oracle eram muito ‘generosas’).

Seria muito difícil acontecer do estrago ser grande. Para que isso ocorresse a falha que ocorreu não poderia acontecer e depois do delete eu ainda precisaria dar um commit.

Se isso tivesse acontecido eu teria mudado de profissão 🙂

/home/osvaldo/icons

Essa foi no meu “início de carreira” com Linux. Eu trabalhava na Conectiva (atual Mandriva) e fiquei responsável por criar um sistema de “temas” para o Conectiva Linux 5.

Os temas deveriam funcionar igualmente no Gnome, KDE e no WindowMaker que, até então, era o WM que eu usava.

Fiz os pacotes, scripts, configurações e enviei para a máquina de integração (mapi). Dei essa tarefa como terminada e passei para as próximas.

Lançado o Conectiva 5 o sistema “explodiu” nas máquinas dos usuários e o sistema de temas não funcionava como deveria em todas as máquinas que usavam o WindowMaker .

Ao analisar o problema vi que no arquivo de configuração do WindowMaker tinha um “/home/osvaldo” no caminho de busca de imagens e ícones que causava a ‘quebra’.

Resultado: as coisas funcionavam perfeitamente na minha máquina e em todas as outras máquinas do mundo que tivessem um usuário “osvaldo”.

Esse foi um bug entre os vários desse sistema de temas que criei e que futuramente (por razões óbvias) foi abandonado.

Se você ainda tem um Conectiva 5 para instalar por aí faça o teste. 😀

Off-topic

Alguns, talvez, ainda não saibam mas eu vivi um período em que queria largar da área de informática e virar publicitário (ok, eu era jovem).

Neste período “publicitário” da minha vida eu trabalhei em uma agência de propaganda chamada DLMRozani e lá nós fazíamos toda a mídia local das várias Lojas Americanas do país (a conta nacional era da agência Talent).

A mídia local inclui anúncios televisivos (para as afiliadas locais), anúncios em jornais locais e aqueles folhetinhos de ofertas que todos já devem ter visto.

Num desses folhetos onde anunciamos carne de 1ª à R$3,90/kg quando o preço correto era algo como R$13,90/kg (não lembro detalhes da oferta).

A sorte que era uma dessas “ofertas relâmpago” (válidas por 1 hora), mas o fato é que vendemos 400kg de carne em 1 hora. Filas se formaram em volta do quarteirão das Lojas Americanas e caminhões não paravam de chegar trazendo carne para ser vendida (aquele blablabla de “enquanto durarem os estoques” não tem muito amparo legal pelo código de consumidor).

Só para contextualizar: nesta época as Lojas Americanas tinham lojas que funcionavam como supermercado em várias cidades do Brasil. Essa operação foi vendida posteriormente para um grupo francês chamado Stoc.

Finalizando

Se eu lembrar de mais algumas “cagadas” eu volto a atualizar esse post mas acho que essas foram as maiores.

E vocês? Fizeram alguma dessas já?

PyConBrasil 2008

Já está quase tudo pronto para o maior evento de Python da América Latina: a PyConBrasil 2008. A cidade escolhida para sediar o evento este ano foi o Rio de Janeiro e o evento irá acontecer nas instalações da Universidade Veiga de Almeira.

O local está definido, a grade com a programação está pronta, o convidado internacional está confirmado (aguardando somente o visto de entrada no Brasil), a divulgação está à todo vapor (você pode ajudar) e as inscrições estão abertas!

Enfim, tudo caminhando para fazer uma PyConBrasil ainda melhor do que a do ano passado em Joinville.

Colabore com a divulgação

Participe da campanha Site Amigo da PyConBrasil e ajude a divulgar o evento.

Além de participar da campanha Site Amigo você também pode fazer um post em seu blog dizendo o que você espera do evento (caso vá participar) ou sobre como você está triste por não poder participar.

Você também pode colocar o link e o banner do evento no feed do seu blog ou ainda pedir para aquele amigo que tem aquele blog famoso (ou trabalha em um jornal/revista/blog famoso) para divulgar o evento.

Adicione links para o evento no seu del.icio.us, compartilhe as notícias sobre o evento no seu Google Reader, comente com seus amigos do Twitter/Jaiku, entre e participe da comunidade no Orkut, faça um vídeo engraçado mostrando como você vai ao evento e coloque no Youtube… enfim… use a imaginação e colabore com o evento! 🙂

Você também pode adesivar o seu carro, ligar pra rádio e convidar os ouvintes, contratar carro de som, levar uma faixa ou bandeira no estádio com os dizeres: “Filma nóis na PyConBrasil Galvão!”, panfletar no semáforo…

E os sites teimam em não funcionar

Recentemente eu comprei um Macbook e passei a ser usuário do Mac OS X. Neste sistema eu tenho acesso a dois browsers extremamente poderosos: o Safari e o Firefox. Ambos em suas últimas versões.

Como uso Linux pro trabalho eu também comprei uma licença do VMWare Fusion (que já valeu cada um dos centavos gastos) onde rodo um Ubuntu Linux e navego com o Firefox.

Isso significa que em meu ambiente tenho à disposição 2 browsers diferentes sendo que um deles roda em 2 sistemas operacionais distintos.

Eu também gastei R$500 com uma licença original do Windows Vista. Em minha opinião esse dinheiro foi perdido porque o sistema é ruim (não porque é o Windows e sim porque é o Vista). Como não uso o Windows pra nada além de eventuais partidas de Counter-Strike (que não podem ser jogadas no VMWare por causa da falta do suporte à aceleração 3D da placa) eu praticamente não inicio ele em minha máquina. Iniciá-lo também é chato e consome muita memória me obrigando quase sempre a ‘desligar’ o Linux.

Em resumo: Iniciar o Windows pra mim é um transtorno.

Nos últimos meses eu tive que interagir via Web com uma grande quantidade de sites governamentais, bancos, empresas de telefonia e até mesmo lojas de e-commerce e me impressionou a quantidade absurdas de sites que ainda teimam em não funcionar com browsers que não sejam o Internet Explorer.

No passado eu entendia essas coisas porque realmente o IE era usado por 99,9% dos clientes na Web mas hoje, apesar de ainda ser uma grande maioria, esse cenário mudou bastante.

Dependendo das estatísticas que se pega na Internet você pode ver que a presença do IE varia entre 74% e 95% e alguns vão se perguntar: o que são 5% dos navegadores? Porque devemos nos preocupar com 5% dos navegadores Firefox/Safari/Opera/…?

Porque numa pesquisa de 2007 o Brasil tinha 15 milhões de pessoas com acesso à Internet e 5% de 15 milhões são 750.000 pessoas! 🙂

Me explique uma coisa: que tipo de empresa despresaria 750.000 potenciais clientes somente porque a sua equipe técnica não quer investir um tempo testando o site em um browser “alternativo” e eventualmente corrigir problemas?

Os empresários brasileiros desprezam 750.000 potenciais clientes e depois dizem que seus negócios estão afundando por conta dos impostos altos…

Segue abaixo uma lista de sites que não funcionam com browsers não-IE:

  • http://www.vivo.com.br – Simplesmente não é possível “logar” na parte de serviços deles para aquelas tarefinhas ‘triviais’ como ver a fatura do celular e afins.
  • http://www.claro.com.br – É impossível acessar qualquer informação referente à sua conta no site deles. Outras partes estão OK.
  • http://www.saraiva.com.br – Não é possível alterar o seu endereço de entrega.
  • Todos aqueles “Atendimento Online” que tentei usar não funcionaram.
  • … se vocês lembrarem de mais algum coloquem nos comentários…

O site do Banco Itaú funciona perfeitamente bem com meu browser, mas a disponibilidade dos serviços deles é patética. Pelo menos duas vezes por semana eu recebo um “Erro do servidor” ou um “Serviço indisponível” na cara.

Mas cabe acrescentar que uma vez fiz uma reclamação pro Itaú sobre uma parte do site que não estava funcionando no Firefox e algumas horas depois me ligaram pedindo detalhes para providenciar o conserto. E consertaram mesmo. Isso foi Nota 10!

Como vocês puderam ver eu tenho o hábito de ligar reclamando desses problemas e sugerindo o conserto dos mesmos, mas tenho certeza de que eles jogam fora esses conselhos porque “só” 5% dos acessos deles vêm de browsers alternativos.

Aos amigos do Terra… eu ia acrescentar o Terra TV aqui mas ele subitamente funcionou no meu Firefox 🙂