Back-End

26 abr, 2012

Dicas, truques e hacks de Python – Parte 3

Publicidade

No artigo anterior, falamos sobre a criação de listas inteligentes. Desta vez, vamos falar da manipulação de dicionários, como aproveitar bem esse recurso e também algumas dicas de como trabalhar com os operadores lógicos true e false.

3 – Dicionários

3.1 – Construindo dicionários com argumentos chaves

Quando eu estava aprendendo Python, eu perdi completamente a alternativa de criar dicionários. Qualquer argumento chave que você passa ao construtor dict é adicionado ao mais novo dicionário criado antes de retornar. Claro que você está limitado às chaves que podem ser feitas dentro dos argumentos chaves: nomes de variáveis válidas no Python. Aqui está um exemplo:

1dict(a=1, b=2, c=3)
2# returns {'a': 1, 'b': 2, 'c': 3}

Isso pode ser um pouco mais limpo que uma criação “regular” de dicionários, dependendo do seu código; existem menos quotes. Eu o uso bastante.

3.2 – Dicionários para listas

Transformar um dicionário em uma lista ou iterador é fácil. Para ter uma lista das chaves, você pode simplesmente elencar o dicionário dentro da lista. É mais limpo que, no entanto, chamar .keys() no dicionário para ter uma lista de chaves, ou .iterkeys()para ter um iterador. Da mesma maneira, você pode chamar .values() ou .itervalues() para ter uma lista ou iterador de valores do dicionário. Lembre-se de que dicionários são inerentemente desordenados e então esses valores não estarão em uma ordem relevante.

Para preservar as chaves e os valores, você pode transformar um dicionário em uma lista ou iterador de 2 itens de tuplas ao usar .items() ou .iteritems(). Isso é algo que você provavelmente fará muito, e não é muito animador:

1dictionary = {'a': 1, 'b': 2, 'c': 3}
2dict_as_list = dictionary.items()
3#dict_as_list now contains [('a', 1), ('b', 2), ('c', 3)]

3.3 – Listas para dicionários

Você pode reverter o processo, transformando uma lista de 2 elementos de listas ou tuplas em um dicionário:

1dict_as_list = [['a', 1], ['b', 2], ['c', 3]]
2dictionary = dict(dict_as_list)
3# dictionary now contains {'a': 1, 'b': 2, 'c': 3}

Você também pode combinar isso com o método “keyword arguments” para criar dicionários, como discutido acima:

1dict_as_list = [['a', 1], ['b', 2], ['c', 3]]
2dictionary = dict(dict_as_list, d=4, e=5)
3# dictionary now contains {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}

A possibilidade de converter um dicionário em lista é algo que vem a calhar, acho. Mas o que realmente o faz ser bem legal é o próximo passo.

3.4 – “Compreensões de dicionários”

Apesar de o Phyton não ter compreesões de dicionários, você pode fazer algo bem próximo com pouca bagunça e código. Apenas use .iteritems() para transformar seu dicionário em uma lista, jogue nele um gerador de expressões (ou lista de compreensão), e então a coloque de volta no dicionário.

Por exemplo, digamos que eu tenho o dicionário name:email pairs, e quero criar um dicionário name:is_email_at_a_dot_com pairs:

1emails = {'Dick': 'bob@example.com', 'Jane': 'jane@example.com', 'Stou': 'stou@example.net'}
2
3email_at_dotcom = dict( [name, '.com' in email] for name, email in emails.iteritems() )
4
5# email_at_dotcom now is {'Dick': True, 'Jane': True, 'Stou': False}

Perfeito. Claro que você não tem que começar E terminar com um dicionário, você pode jogar umas listas ali também.

Enquanto isso, é um pouco menos legível que uma lista de compreensão em ordem, eu argumentaria que ainda é melhor que um loop for massivo.

4 – Selecionando valores

4.1 – The right way (o jeito certo)

Enquanto eu estava escrevendo este artigo, eu vacilei sobre qual seria o right way (jeito certo) de selecionar valores inline, nova no Python 2.5 (você achou que teria mais alarde!). O Python agora suporta a sintaxe ‘value_if_true if test else value_if_false’. Então, você pode fazer uma simples seleção de valores em uma linha, sem nenhuma sintaxe estranha ou ressalvas importantes:

1test = True
2# test = False
3result = 'Test is True' if test else 'Test is False'
4# result is now 'Test is True'

Ok, ainda está um pouco feio. Você também pode encadear múltiplos testes em uma linha:

1test1 = False
2test2 = True
3result = 'Test1 is True' if test1 else 'Test1 is False, test2 is True' if test2 else 'Test1 and Test2 are both False'

O primeiro if/else é avaliado primeiro e, se o if test1 é falso, o segundo if/else é avaliado. Você também pode fazer coisas mais complicadas, especialmente se colocar alguns parênteses.

Nota pessoal

Isso é muito novo no campo, e minha reação é misturada. Realmente é o Right Way, é mais limpo, e eu gosto… mas ainda é feio, especialmente se você tem múltiplos if/else’s aninhados.

Claro que a sintaxe para toda a seleção de truques de valor é feia.

Tenho uma quedinha pelo truque and/or abaixo, eu o acho bastante intuitivo, agora que entendo como ele funciona. E não é menos eficiente que fazer as coisas do Right Way. 

O que você acha? Sinta-se à vontade para comentar abaixo.

Apesar de a linha and/or ser o novo método correto, você deveria checar os truques abaixo. Mesmo se você estiver planejando programar no Python 2.5, ainda vai encontrá-los em códigos antigos. Claro, se você precisar de compatibilidade ou não tem o Python 2.5, você realmente precisa checar os truques abaixo.

4.2 – O truque and/or

No Python, “and” e “or” são criaturas complexas. Usar o “and” para juntar duas expressões não simplesmente retorna true, se ambos são verdadeiros, e false, se ambos são falsos. Em vez disso, o “and” retorna o primeiro valor falso, ou o último valor se todos são verdadeiros. Em outras palavras, se o primeiro valor é falso, ele é retornado, senão, o último valor é retornado. O resultado é algo que você já deve imaginar: se ambos são verdadeiros, o último valor, que é verdadeiro, será retornado avaliado como true em um boolean test (p.e. um ‘if’ statement). Se algum valor é falso, esse valor será retornado e avaliado como false em um boolean test.

Usar o “or” em duas expressões juntas é similar. O “or” devolve o primeiro valor verdadeiro, ou o último valor se todos forem falsos. Em outras palavras, se o primeiro valor é verdadeiro, ele é retornado, senão, o último valor é retornado. Se ambos são falsos, o último valor, que é falso, será retornado e avaliado como false em um boolean test. Se algum valor é verdadeiro, esse valor será retornado e avaliado como true em um boolean test.

Isso não te ajuda muito se você está apenas testando a “verdade”. Mas você pode usar “and” e “or” para outros objetivos no Python; o meu favorito é selecionar entre os valores de uma maneira parecida à do C ternary conditional assignment operator ‘test ? value_if_true : value_if_false’:

1test = True
2# test = False
3result = test and 'Test is True' or 'Test is False'
4# result is now 'Test is True'

Como isso funciona? Se o test é true, o statement and o pula e o devolve pela metade, aqui ‘Test is True’ or ‘Test is False’. Como o processo continua da esquerda para a direita, o statement or retorna o primeiro valor verdadeiro, ‘Test is True’.

Se o test é false, o statement and devolve test. Como o processo continua da esquerda para a direita, o statement que resta é test or ‘Test is False’. Como o test é false, o statement or o pula e retorna sua metade da direita, ‘Test is False’.

Cuidado

Garanta que o valor do meio (if_true) nunca seja falso. Se for, o statement ‘or’ vai sempre pulá-lo, e sempre retornará o valor mais à direita (if_false), não importando qual seja o valor de teste.

Depois de me acostumar com esse método, ‘The Right Way‘ acima se tornou menos intuitivo para mim. Se você não está preocupado com compatibilidade, sugiro que você tente ambos e veja qual dos dois você prefere. É bem simples aninhar truques “and/or” ou jogar um “and” ou “or” extras depois que você entende a lógica por trás deles. Se você não consegue decidir, ou não quer aprender os dois, então use o “and/or”. Faça as coisas do Right Way, e termine com isso.

Claro que, se você precisa suportar as versões do Python abaixo da 2.5, ‘The Right Way’ não vai funcionar. (Eu estava tentado a dizer que era o ‘The Wrong Way’ – ‘ O Jeito Errado). Nesse caso, o truque “and/or” é definitivamente sua melhor aposta para a maioria das situações.

Espero que isso faça sentido; é difícil de explicar. Pode parecer complicado agora, mas se você usá-lo algumas vezes e brincar com o  “and” ou “or”, rapidamente eles vão fazer sentido e você será capaz de criar novos truques “and” ou “or” você mesmo.

4.3 – Usando True e False como indexes

Um outro jeito de selecionar valores é usar True e False como indexes de uma lista, tirando vantagem do fato que False == 0 e True == 1:

1test = True
2# test = False
3result = ['Test is False','Test is True'][test]
4# result is now 'Test is True'

Isso é um pouco mais avançado que o truque do “and” ou “or”, e livre do problema em que o value_if_true deve ser, ele próprio, verdadeiro.  

No entanto, ele também sofre de um defeito significante: ambos os itens devem ser avaliados antes de sua “verdadeirabilidade” ser checada. Para strings ou outros itens simples, isso não é um problema. Mas se cada item envolver uma computação significante ou I/O, você realmente não quer ter trabalho dobrado. Por esse motivo, eu normalmente prefiro o ‘Right Way’ ou o truque do “and” ou “or”.

Note também que o método do index só funciona quando você sabe que o teste é False ou True (ou 0 ou 1, mas nenhum outro numero inteiro ou objeto arbitrário). Caso contrário, você deveria escrever bool(test) em vez de test para obter o mesmo comportamento da expressão do truque “and” ou “or” acima.

No próximo artigo, falaremos sobre a declaração de funções no Python.

?

Texto original licenciado sob Creative Commons e disponível em http://www.siafoo.net/article/52