You are currently browsing Osvaldo Santana's articles.

Palavras-chave: Python, pegadinhas, gotchas, pitfalls, listas, extends

Em Python o operador de soma e atribuição += pode ser usado na forma “a += 1” que tem o mesmo significado que a expressão “a = a + 1“.

Até aí nada de diferente. Mas a linguagem Python disponibiliza uma função própria para tratar o operador += (__iadd__) e outra para o operador de adição + (__add__). Na maioria das vezes o método __iadd__ chama __add__ e tudo fica certo. Mas não é isso que acontece com as listas.

Nos objetos do tipo list o método .__iadd__() irá chamar o método .extend() da lista. Desta forma podemos fazer coisas como:

>>> a = [1,2,3]
>>> a += [4,5,6]
>>> a
[1, 2, 3, 4, 5, 6]
>>> a.extend([7,8,9])
>>> a
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a = [1,2,3]
>>> a + [4,5,6]
[1, 2, 3, 4, 5, 6]

As coisas começam a ficar complicadas quando temos uma situação que envolve strings, como no caso seguinte. Lembre-se que strings também são consideradas seqüências em Python.

>>> a = [1,2,3]
>>> a + "spam"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  TypeError: can only concatenate list (not "str") to list
>>> a += "spam"
>>> a
[1, 2, 3, 's', 'p', 'a', 'm']
>>> a.extend("eggs")
>>> a
[1, 2, 3, 's', 'p', 'a', 'm', 'e', 'g', 'g', 's']

Os exemplos acima deixam claro que é preciso tomar cuidado com esse tipo de situação em seus programas.

Alguns argumentam que esse comportamento da lista não é muito consistente e que esse “problema” deveria ser consertado. Mas existe um outro grupo que diz que esso é o comportamento correto. Até que esses grupos entrem num acordo é bom ficarmos de olho.

Palavras-chave: Python, Design Patterns, Padrões de Projeto, OOP, POO

Um Singleton é um dos padrões de projeto descrito no famoso livro “Design Patterns” escrito pela GoF (Gang of Four) e o seu funcionamento é bastante simples: uma classe é chamada de Singleton quando ela permite apenas uma única instância.

A maneira de se implementar um Singleton descrita no livro consiste em definir um método get_instance() na classe que ficará responsável por criar a primeira instância e a partir daí retorná-la sempre que for chamada.

Mas a natureza dinâmica de Python e seu sistema de meta-classes permitem que se crie uma classe chamada Singleton genérica que podemos usar como super-classe dos nossos próprios Singletons:

class Singleton(object):
   def __new__(cls, *args, **kwargs):
      if '_inst' not in vars(cls):
         cls._inst = type.__new__(cls, *args, **kwargs)
      return cls._inst

Agora, para que a sua classe se transforme em Singleton basta apenas derivá-la desta classe e não é mais necessário implementar o método get_instance(). O seu uso ficaria assim:

class MeuSingleton(Singleton):
   pass

a = MeuSingleton()
b = MeuSingleton()

print a is b # imprimirá True, pois trata-se da mesma instância

Essa dica faz parte do livro Python Cookbook e foi escrita por Jürgen Hermann. No livro você poderá encontrar as explicações detalhadas sobre essa classe e mais uma série de outras dicas interessantes sobre diversos assuntos.

Palavras-chave: Python, listas, cópia, objetos

Em Python as variáveis não armazenam os objetos quando atribuímos um valor à elas. Ao invés disso uma variável em Python guarda apenas uma referência para o objeto em questão. Uma prova disto pode ser vista a seguir:

>>> a = [1,2,3]
>>> b = a
>>> b is a
True
>>> a.append(4)
>>> b
[1, 2, 3, 4]

Por essa razão precisamos fazer cópias dos objetos quando queremos manipular os dados de uma das variáveis sem afetar os dados da outra.

A biblioteca Python disponibiliza o módulo copy para essa tarefa, mas em casos de listas (ou qualquer outra seqüência como strings e tuplas) existe uma maneira mais simples de se fazer isso: utilizando slicing, veja:

>>> a = [1,2,3]
>>> b = a[:]
>>> a is b
False
>>> a.append(4)
>>> b
[1, 2, 3]
>>> a
[1, 2, 3, 4]

A operação de slicing retornou uma cópia idêntica da lista porque a[:] é o mesmo que a[0:len(a)]. Vale lembrar, porém, que apenas a lista é copiada e não os objetos que estão dentro dela.

Palavras-chave: Python, command prompt, prompt de comandos, prompt, DOS, Windows

Quando executamos um programa “.py” no Windows uma janela de prompt irá aparecer automaticamente para que as eventuais saídas de texto do seu programa sejam enviadas para lá.

Mas essa janela pode se tornar incoveniente quando o nosso programa já provê uma interface gráfica pois, neste caso, teríamos duas janelas abertas para a mesma aplicação.

A janela aparece porque a extensão “.py” está associada ao interpretador “X:\PythonXX\python.exe” que está configurado para abrir uma janela de prompt.

Caso você precise executar seu programa sem essa janela é melhor usar o interpretador “X:\PythonXX\pythonw.exe” que é feito justamente para esse propósito.

A extensão “.pyw” é associada à esse interpretador, portanto, basta renomear os seus arquivos .py, .pyc e .pyo para .pyw que as janelas de prompt deixarão de aparecer.

Palavras-chave: Python, dicionários, switch, case

Algumas pessoas reclamam de que Python não possui uma instrução switch/case, que permite executar trechos de código dependendo do valor de uma determinada variável.

Python realmente não disponibiliza essa funcionalidade, mas podemos simulá-la utilizando funções e dicionários. Para isso basta lembrar que funções e métodos em Python também são objetos. Veja:

def func1(): print "func1"
def func2(): print "func2"
def func_default(): print "default"

switch = {
   1: func1,
   2: func2,
}

for case in range(3):
   try:
      # chamamos a funcao aqui
      switch[case]()
   except KeyError, e:
      # se nao existe, chamamos o default
      func_default()

Quando executarmos este exemplo o resultado será:

default
func1
func2

Palavras-chave: Python, listagem, cores, zebrado, zebra list

Em aplicações Web é bastante comum exibir grandes listagens em uma página. Para facilitar a leitura dessas listagens, quase sempre utilizamos do artifício de alternar as cores de fundo das linhas exibidas.

O meu amigo Ulysses, mais uma vez (estou até pensando em deixá-lo escrevendo por aqui) deu uma dica excelente para fazer isso, veja:

cores = ["#F5F5F5", "#FFFFFF"]
for n, linha in enumerate(linhas):
   cor_fundo = cores[n % 2]
   # aqui vai o seu código...

Vou aproveitar também essa dica para embutir uma outra dica mais voltada para a “usabilidade”: a diferença de saturação entre uma cor e outra usada no exemplo acima deve ser pequena para tornar esse efeito efetivo.

Palavras-chave: Python, or, in, if, while, condicional, pythonico

Quem está começando no mundo do desenvolvimento Python e não conhece muito a linguagem, costuma criar cláusulas condicionais muito longas utilizando o operador “or”:

if a == "foo" or a == "bar" or a == "baz":
   print a

Imagine o tamanho dessa cláusula quando precisar comparar o valor de “a” com muitos outros valores. Para essas situações você pode usar o operador “in” do Python que verifica se há uma ocorrência do objeto em uma lista:

if a in ["foo", "bar", "baz"]:
   print a

Ficou bem mais simples e legível, não? Esta é a maneira “pythônica” de se montar esse tipo de cláusula condicional.

Essa dica foi sugerida pelo meu amigo Ulysses que me atentou para o fato de que os novatos na linguagem costumam fazer esse tipo de cláusula da forma inadequada.

Palavras-chave: testes, unittest, docstring, documentação, testes unitários, rest, docutils

Todo desenvolvedor sabe que fazer testes é superimportante e que fazer documentação também é algo que melhora a qualidade de seus produtos. Mas num primeiro momento, fazer testes é trabalhoso e documentar o nosso código também costuma ser muito chato.

E se a gente conseguisse fazer as duas coisas ao mesmo tempo e obter bons testes e boa documentação? Com o módulo doctest do Python é possível fazer isso. Veja o exemplo abaixo (que foi parcialmente tirado da documentação do módulo):

# -*- coding: utf-8 -*-
import doctest

def fatorial(valor):
    """fatorial(int) -> int
    A função fatorial pode ser usada para calcular
    o fatorial de um número n onde n > 0 vejam os exemplos::
       >>> fatorial(15)
       1307674368000L
       >>> fatorial(-1)
       Traceback (most recent call last):
       ...
       ValueError: valor precisa ser > 0
       >>> fatorial(15.1)
       Traceback (most recent call last):
       ...
       ValueError: valor precisa ser inteiro
    """
    if valor < 0:
        raise ValueError("valor precisa ser > 0")

    if not isinstance(valor, (int,long)):
        raise ValueError("valor precisa ser inteiro")

    if valor:
        return valor * fatorial(valor - 1)
    return 1

if __name__ == '__main__':
    doctest.testmod()

Agora, para rodar nossos testes basta executar esse exemplo:

$ python fatorial.py -v
:
1 items passed all tests:
   3 tests in __main__.fatorial
3 tests in 2 items.
3 passed and 0 failed.
Test passed.

Além de colocar os testes dentro das docstrings também podemos usar arquivos texto convencionais para essa mesma finalidade. A documentação pode usar o formato ReST usado pelos utilitários docutils.

Add to Technorati Favorites

Assuntos