Dict comprehensions

Anteriormente, escrevi sobre as list comprehensions, que são um recurso bem interessante da linguagem Python (se você não leu, veja ). Elas permitem que criemos listas usando uma sintaxe muito simplificada. Assim como existem as list comprehensions para listas, existem os dict comprehensions para dicionários. A sintaxe é bem simples. Se quisermos criar um dicionário com base nos valores de uma lista, onde cada elemento da lista será chave para um valor que representa o dobro de si, podemos utilizar a seguinte estrutura:

>>> d = {i:i**2 for i in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]}
>>> print d
{1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81, 10: 100}

Para fazermos um dicionário que utilize como chave o número inteiro que representa o código ASCII de determinado caractere e que tenha tal caractere como respectivo valor, podemos usar o seguinte dict comprehension:

>>> {i : chr(i) for i in range(65, 91)}
{65: 'A', 66: 'B', 67: 'C', 68: 'D', 69: 'E', 70: 'F', 71: 'G', 72: 'H', 73: 'I', 74: 'J', 75: 'K', 76: 'L', 77: 'M', 78: 'N', 79: 'O', 80: 'P', 81: 'Q', 82: 'R', 83: 'S', 84: 'T', 85: 'U', 86: 'V', 87: 'W', 88: 'X', 89: 'Y', 90: 'Z'}

Mais em: http://www.python.org/dev/peps/pep-0274/

Customizando o IPython

Dias atrás estive tentando descobrir como trocar o prompt-padrão do IPython [1]. Para quem não sabe, o prompt-padrão de comandos do IPython é:

In [x]:

Onde x é um contador de entradas fornecidos pelo usuário. O que eu queria era substituir tal prompt pelo prompt padrão do shell Python:

>>>

Para isso, é preciso antes criar um perfil de configurações de usuário para o IPython, com o comando (em um shell Linux):

$ ipython profile create

Assim, será criado um diretório com as configurações do IPython em seu diretório de usuário, onde está armazenado o arquivo de configurações ipython_config.py (no meu caso está em ~/.config/ipython/profile_default/ipython_config.py).

Para substituir o prompt do IPython, procure pela linha com o seguinte conteúdo dentro do arquivo de configurações e descomente-a:

# c.PromptManager.in_template = 'In [\\#]: '

Após descomentar tal linha, substitua o valor ‘In [\\#]: ‘ por ‘>>> ‘, para que tal linha fique como a linha abaixo:

c.PromptManager.in_template = '>>> '

Uma coisa que acho irritante é o IPython sempre pedindo confirmação quando desejo fechá-lo, após ter pressionado ^-d. Para evitar que ele pergunte se você deseja mesmo fechá-lo, basta descomentar a seguinte linha no mesmo arquivo e trocar o valor True para False:

# c.TerminalInteractiveShell.confirm_exit = True

A linha deverá ficar assim:

c.TerminalInteractiveShell.confirm_exit = False

Além dessas, existem muitas coisas que podem ser personalizadas no IPython. Você pode vê-las comentadas no arquivo supracitado. Para personalizar outros aspectos relacionados ao prompt de comandos, vá até a seção “PromptManager configuration” do arquivo e altere de acordo com o seu gosto.

[1] http://ipython.org

Trabalhando com datas e horas em Python – datetime

Módulos utilizados nesse post:

  • date
  • datetime
  • timedelta

Esse post vai mostrar alguns exemplos simples de como utilizar o módulo datetime [1] para manipularmos, em Python, dados que representam datas. Antes de qualquer coisa, vou enumerar algumas operações que são corriqueiras quando precisamos lidar com datas:

  1. Obter a data atual;
  2. Obter a data antes ou após X dias da data atual;
  3. Calcular a diferença de dias entre duas datas;
  4. Descobrir o dia da semana em que determinada data cai;

Agora vamos ver como resolvê-las, usando o módulo datetime.

1. Obter a data atual

O módulo datetime possui dentro de si uma classe date [2] definida. Nessa classe, existem alguns métodos para manipulação de datas, como a função today, que retorna um objeto do tipo datetime.date.

>>> from datetime import date
>>> hj = date.today()
>>> print hj
2012-07-10
>>> print hj.day
10
>>> print hj.month
07
>>> print hj.year
2012

2. Obter a data há ou daqui a X dias

Para isso, iremos converter primeiramente a nossa data em um número ordinal, através do método toordinal(), que nos retorna a quantidade de dias passados desde o dia 1/1/1 até a data recebida como argumento. Depois disso, basta somar (ou subtrair) a esse número inteiro o número de dias da diferença que queremos calcular e então reconverter o inteiro para data, através do método fromordinal(). Abaixo, obtivemos a data a daqui exatos 45 dias.

>>> from datetime import date
>>> hj = date.today()
>>> print hj.toordinal()
734694
>>> futuro = date.fromordinal(hj.toordinal()+45) # hoje + 45 dias
>>> print futuro
2012-08-24

3. Calcular a diferença de dias entre datas

Para realizar essa, vamos obter as duas datas entre as quais queremos saber o intervalo de dias e depois usar o operador de subtração (-) para fazer a operação. O operador subtração, quando aplicado a datas, retorna um objeto do tipo timedelta, contendo a diferença entre as datas. Esse objeto possui um atributo chamado days, que obviamente nos dá o número de dias representados pelo delta.

>>> from datetime import date
>>> hj = date.today()
>>> print hj.toordinal()
734694
>>> futuro = date.fromordinal(hj.toordinal()+45) # hoje + 45 dias</pre>
>>> diferenca = futuro - hj
>>> print diferenca.days
45

4. Descobrir o dia da semana de uma data

Essa é fácil. Após construir uma data, podemos chamar o método weekday() do objeto date. Ele retornará um número inteiro entre 0 (represendo segunda-feira) e 6 (representando domingo).

>>> from datetime import date
>>> hj = date.today()
>>> print hj.weekday()
1
Para que apareça o dia da semana por extenso, em português, podemos usar uma tupla para armazenar os dias da semana, de acordo  com os valores retornados pelo método weekday().
>>> from datetime import date
>>> hj = date.today()
>>> dias = ('Segunda-feira', 'Terça-feira', 'Quarta-feira', 'Quinta-feira', 'Sexta-feira', 'Sábado', 'Domingo')
>>> print "Hoje é", dias[hj.weekday()]
Hoje é, Terça-feira

Os módulos datetime e date fazem muito mais do que o que foi mostrado aqui. Agora, acesse a documentação dos módulos e faça você mesmo alguns testes.

[1] http://docs.python.org/library/datetime.html

[2] http://docs.python.org/library/datetime.html#datetime.date

Um contador de elementos em sequências

Às vezes precisamos realizar a contagem do número de aparições de determinados elementos em uma sequência, como uma string ou uma lista. Por exemplo, quero saber quantas vezes cada letra apareceu em uma string. Como fazer isso em Python? Primeiro, vamos fazer “na mão” e depois vamos conhecer uma solução “pronta”.

def conta_ocorrencias(s):
    ocorrencias = {}
    for c in s:
        if c in ocorrencias:
            ocorrencias[c] = ocorrencias[c] + 1
        else:
            ocorrencias[c] = 1
    return ocorrencias

A solução acima apresentada utiliza um dicionário armazenando como chaves as letras encontradas e, como valor para cada letra, a quantidade de ocorrências desta. Percorremos a string s letra por letra e, se a letra atual já estiver sendo usada como chave no dicionário ocorrencias, o valor correspondente a tal letra é incrementado em um (ou seja, encontramos mais uma ocorrência da letra na string recebida). Se a letra ainda não estiver aparecendo como chave do dicionário, então é criada uma entrada neste com a letra sendo usada como chave e o valor associado 1 (ocorrencias[c] = 1). Vamos analisar o teste feito dentro do for:

...
        if c in ocorrencias:
            ocorrencias[c] = ocorrencias[c] + 1
        else:
            ocorrencias[c] = 1
...

Esse teste é necessário porque se tentarmos acessar uma chave inexistente de um dicionário, é retornado um KeyError. Assim, precisamos testar para verificar se a letra atual já foi anteriormente inserida no dicionário ou não. Se foi, daí sim podemos incrementar o valor associado. Se não foi, daí temos que incluir tal valor com o número 1 associado.

Para simplificar isso, podemos usar o módulo collections [1]. Esse módulo nos fornece um tipo especial de dicionário chamado de defaultdict [2]. Esse dicionário permite que especifiquemos, ao construir um dicionário, uma função que será chamada para retornar um valor padrão para quando a chave solicitada não existir no dicionário. Com ele, é possível fazer o seguinte:

    ocorrencias = collections.defaultdict(int)
    ...
        ocorrencias[c] = ocorrencias[c] + 1
    ...
A função int(), quando chamada sem argumentos, retorna 0. Como passamos ela ao construtor do dicionário ocorrências, é ela que será chamada quando houver um acesso a uma chave inexistente. Isso possibilita que usemos esse valor para fazer a soma no código acima. Na primeira vez que é executado para determinado caractere, o código acima será executado como se fosse:
    ocorrencias[c] = 0 + 1
Nas vezes seguintes, ao invés de 0 (valor obtido ao tentarmos acessar o valor de uma chave inexistente), teremos como valor o número atual de ocorrências para o caractere contido na variável c. O código completo segue abaixo:
import collections
def conta_ocorrencias(s):
    ocorrencias = collections.defaultdict(int)
    for c in s:
        ocorrencias[c] = ocorrencias[c] + 1
    return ocorrencias

E a solução “pronta”?

O jeito mais simples de fazer a contagem de ocorrências dos elementos de uma sequência é através da classe Counter [3], também presente no módulo collections. Vamos ver um exemplo de utilização:

>>> import collections
>>> s = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut"
>>> c = collections.Counter(s)
>>> print c.most_common(5)
[(' ', 13), ('i', 11), ('e', 8), ('t', 8), ('d', 7)]

Um dos métodos mais interessantes é o most_common() [4], que retorna uma lista contendo os elementos mais comuns na sequência. Se passarmos um número n como argumento, ele irá retornar os n valores mais comuns na sequência, como no exemplo acima.

Além do Counter e do defaultdict, o módulo collections fornece várias outras estruturas úteis, que veremos em posts futuros.

[1] http://docs.python.org/library/collections.html

[2] http://docs.python.org/library/collections.html#collections.defaultdict

[3] http://docs.python.org/library/collections.html#collections.Counter

[4] http://docs.python.org/library/collections.html#collections.Counter.most_common