x = 7
paridade = "par" if x % 2 == 0 else "ímpar"
paridade'ímpar'
Esta seção corresponde a Control Flow, Truthiness, Sorting e List Comprehensions, do capítulo 2 de Grus (2019).
Que você conhece if, while e for. O que vale desacelerar: compreensões de lista e ordenação com key — as duas construções que mais mudam a aparência do código do livro em relação ao que você escreveria numa linguagem sem elas.
if, elif, else funcionam como esperado, e existe uma forma ternária de uma linha:
for percorre iteráveis diretamente, sem índice:
Python tem regras de conversão para booleano que valem conhecer, porque o código do livro se apoia nelas.
São falsos: False, None, [], {}, "", set(), 0, 0.0. Praticamente tudo mais é verdadeiro — o que permite escrever if lista: no lugar de if len(lista) > 0:.
is None, não == None
True
A diferença importa quando o objeto define o próprio __eq__ — arrays e DataFrames, por exemplo, comparam elemento a elemento e devolvem um array, não um booleano. is compara identidade e nunca é sequestrado.
Duas funções que aparecem bastante:
(True, False, True, False, True)
Repare no caso vazio: all([]) é True e any([]) é False. É consequência da definição — “todos os elementos satisfazem” é vacuamente verdadeiro quando não há elementos.
keyToda lista tem sort(), que ordena no lugar, e existe sorted(), que devolve uma lista nova. Mas a parte que importa é o parâmetro key:
([1, 2, 3, 4], [4, 3, 2, 1], [-4, 3, -2, 1])
key recebe uma função que, dado um elemento, devolve o valor pelo qual comparar. É onde os lambda do livro moram:
from collections import Counter
document = ["o", "rato", "roeu", "a", "roupa", "do", "rei", "de", "roma",
"o", "rei", "de", "roma", "roeu", "o", "rato"]
word_counts = Counter(document)
# ordena as palavras da mais frequente para a menos frequente
wc = sorted(word_counts.items(),
key=lambda word_and_count: word_and_count[1],
reverse=True)
wc[:4][('o', 3), ('rato', 2), ('roeu', 2), ('rei', 2)]
Esse é exatamente o padrão do Capítulo 1, quando ordenamos os usuários da DataSciencester por número de amigos para achar os “conectores”.
Ordenar por um critério derivado — em vez de ordenar o próprio valor — é a operação que mais aparece em análise de dados. Vale internalizar a forma.
Aqui vale desacelerar de verdade. Compreensões são a construção que mais muda a aparência do código.
A ideia: transformar ou filtrar uma coleção em uma expressão só.
([0, 2, 4], [0, 1, 4, 9, 16], [0, 4, 16])
Funciona também para dicionários e conjuntos:
({0: 0, 1: 1, 2: 4, 3: 9, 4: 16}, {1})
Quando não se usa o valor do laço, a convenção é chamá-lo de _:
Uma compreensão pode ter vários for, e os posteriores podem usar o resultado dos anteriores:
pairs = [(x, y)
for x in range(10)
for y in range(10)]
increasing_pairs = [(x, y)
for x in range(10)
for y in range(x + 1, 10)] # y depende de x
assert len(pairs) == 100
assert len(increasing_pairs) == 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1
assert all(x < y for x, y in increasing_pairs)
len(pairs), len(increasing_pairs)(100, 45)
Compreensão não é açúcar sintático para deixar o código curto — é uma forma de dizer o que você quer em vez de como obter.
Compare: um laço com append descreve um procedimento de construção. [x * x for x in xs if x > 0] descreve o conjunto resultante. Quando a operação é uma transformação ou um filtro, a segunda forma é mais fácil de ler e mais difícil de errar — não há índice, não há acumulador, não há estado.
Quando a operação não é uma transformação simples, o laço continua sendo a escolha certa. Compreensão aninhada de três níveis com condição é ilegível, e o Zen do Python não recomenda isso.