set, dict e comprehensions

Set, dict e alguns tópicos avançados de list

Vimos conceitos básicos de alguns tipos de dados nativos do Python, string, list e tuple. Vale lembrar que list é um tipo de dados super poderoso e que usaremos frequentemente. Por isto gaste algum tempo nos documentos oficiais do Python vendo as muitas funções da classe list aqui.
Neste notebook vamos conhecer outros dois tipos de dados:
  1. set que representa conjuntos
  2. dict que representa "dicionários", melhor explicado logo a seguir
Relembrando, todos os tipos de dados possuem:
  1. uma coleção de métodos inerentes ao tipo, ou seja funções que podem ser chamadas colocando um ponto ao lado de referência a um objeto do tipo seguido do nome do método e argumentos entre parênteses
  2. diversas formas de construir objetos do tipo inclusive função "nome_do_tipo"():
    1. str(arg) para string, onde arg = argumento
    2. tuple(arg) para tuplas
    3. list(arg) para listas
In [131]:
s = str('abc')
t = tuple('123')
print(s,t)
l = list()
print(l)
abc ('1', '2', '3')
[]

set

Este tipo de dados representa conjuntos, ou seja, coleções de elementos quaisquer não-ordenados e sem indexação. Como todo tipo de dados set também tem uma coleção de métodos associados e formas de construir suas instâncias.
In [132]:
c = set('abcdefgx')        # constroi conjunto a partir de umn string       
c1 = set('dybkl')
print(c)
print(c1)
c2 = {'d', 'e', 'r', 'w'}   # constroi conjunto usando fila de elementos entre chaves 
print(c2)
{'g', 'a', 'b', 'e', 'x', 'd', 'c', 'f'}
{'b', 'k', 'l', 'd', 'y'}
{'d', 'e', 'r', 'w'}
Vejamos alguns métodos de set:
In [133]:
d = set()           # constroi um conjunto vazio
d.add(1)            # note que d é modificado
d
Out[133]:
{1}
In [134]:
d.add(2)
d
Out[134]:
{1, 2}
In [135]:
d.add(1)
d
Out[135]:
{1, 2}
Hm, observe que um conjunto não repete elementos, ou seja só elementos únicos podem ser membros.
Vejamos operações tradicionais de conjuntos, que em Python são representadas por operadores matemáticos:
In [136]:
c - c1      # elementos de c que não estão em c1 (complemento em conjuntos)
Out[136]:
{'a', 'c', 'e', 'f', 'g', 'x'}
In [137]:
c | c1      # elementos de c e elementos de c1 (união dos conjuntos)
Out[137]:
{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'k', 'l', 'x', 'y'}
In [138]:
c & c1      # elementos em c e em c1 (interseção de conjuntos)
Out[138]:
{'b', 'd'}
In [139]:
c ^ c1       # elementos em c ou em c1 mas não em ambos (complemento da interseção)
Out[139]:
{'a', 'c', 'e', 'f', 'g', 'k', 'l', 'x', 'y'}
Estas operações de conjuntos também podem ser efetivadas por métodos de nomes sugestivos:
In [140]:
c.union(c1)
Out[140]:
{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'k', 'l', 'x', 'y'}
In [141]:
c.intersection(c1)
Out[141]:
{'b', 'd'}
In [142]:
c.difference(c1)
Out[142]:
{'a', 'c', 'e', 'f', 'g', 'x'}
In [143]:
c.symmetric_difference(c1)
Out[143]:
{'a', 'c', 'e', 'f', 'g', 'k', 'l', 'x', 'y'}
Elementos podem ser removidos pelo metodo discard() com o elemento de argumento:
In [144]:
g = c - c1
g.discard('e')
g
Out[144]:
{'a', 'c', 'f', 'g', 'x'}
O método in funciona também em set e é uma grande mão na roda:
In [145]:
'x' in g
Out[145]:
True
In [146]:
'e' in g
Out[146]:
False
Outros métodos deste tipo de dados podem ser vistos na documentação oficial do Python 3.
E para que servem conjuntos? Conjuntos são uma forma útil de raciocinar e modelar um procedimento sempre que temos uma coleção de entidades de interesse na qual a ordem das entidades é irrelevante.

dict

Este tipo de dados modela "dicionários". Dicionários são conjuntos de pares chave-valor, ou seja temos alí um conjunto de valores e a cada um destes valores existe uma chave única associada, formando um conjunto de chaves.
Dicionários são modificáveis, obedecendo as seguintes regras:
  1. valores podem ser alterados à vontade
  2. chaves não podem ser modificadas
  3. pares chave-valor podem ser removídos
  4. pares chave-valor podem ser adicionados desde que a chave seja única.
Python otimiza muito dicionários para fazer buscas por chave. Achar um valor dada sua chave é bem rápido mesmo para dicionários muito grandes. Por outro lado dado um valor achar sua(s) chave(s) associada(s) pode ser lento.
In [147]:
di = dict()    #cria um dicionário vazio
print(di)
di['cor'] = 'azul'    # assim se adiciona um par chave-valor
di
{}
Out[147]:
{'cor': 'azul'}
In [148]:
outro_dic = {'cargo':'deputado', 'partido': 'PRONA', 'bancada': 'Bala'}  # outra forma de construir dicionario
outro_dic
Out[148]:
{'bancada': 'Bala', 'cargo': 'deputado', 'partido': 'PRONA'}
Para remover um item do dicionário pode ser usada a função del() do Python:
In [149]:
tmp = {1:'a', 2:'b', 3:'c',4:'lixo'}      # desejo remover o item com chave == 4
del(tmp[4])
tmp
Out[149]:
{1: 'a', 2: 'b', 3: 'c'}
Para usar o operador in precisamos especificar se queremos procurar nas chaves ou nos valores:
In [150]:
'PRONA' in outro_dic.values()
Out[150]:
True
In [151]:
'PRONA' in outro_dic.keys()
Out[151]:
False
In [152]:
'cor' in di.keys()
Out[152]:
True
Tanto dict.keys() quanto dict.values() retornam listas dos respectivos dados e por isto o operador in funciona como esperado.
Outro componente dos dicionários é o objeto dict.items(), que permite examinar todos pares chave-valor:
In [153]:
outro_dic.items()
Out[153]:
dict_items([('bancada', 'Bala'), ('partido', 'PRONA'), ('cargo', 'deputado')])
In [154]:
list(outro_dic.items())[2]      # transforma dict_items numa lista e extrai o item de indice 2
Out[154]:
('cargo', 'deputado')
In [155]:
list(outro_dic.items())[2][1]   # quero somente o cargo  
Out[155]:
'deputado'
Dicionários são utilíssimos e possuem um monte de métodos associados. Como sempre consulte a documentação oficial do Python na medida do necessário.
Em particular existe um método embutido no Python chamado zip() que permite "zipar" duas listas criando uma nova lista de pares, perfeito para criar um dicionário.
In [156]:
cores = ['azul', 'amarelo','verde', 'vermelho','laranja', 'preto','branco']
chaves = [1,2,3,4,5,6,7]
dic = dict(zip(chaves, cores))
print(dic)
{1: 'azul', 2: 'amarelo', 3: 'verde', 4: 'vermelho', 5: 'laranja', 6: 'preto', 7: 'branco'}
Onde isto é útil? Por exemplo imagine que lemos um arquivo Excel ou tabela de banco de dados extraindo diversas colunas com centenas de itens que queremos manipular. Usando zip() montamos facilmente um dicionário e daí fazemos pesquisas rápidas sobre o conteúdo das colunas. Um pequeno e muito eficiente script, montado facilmente.
Os valores num dicionário podem ser quelquer tipo de dado do Python, inclusive outros dicionários. Isto é muito da hora, como veremos bem mais adiante em web scraping de dados do Legislativo.

Tópicos (um pouco) avançados

Listas são nosso principal instrumento de trabalho. Listas modelam perfeitamente o paradigma onde temos um conjunto de elementos, possivelmente ordenados, e queremos aplicar algum procedimento em cada um destes elementos sucessivamente.
Python possue um conceito útil para isto, que como tudo que é bom deve ser usado, mas com moderação. Este conceito é chamado de comprehensions e se você achar que lembra lambda expressions isto não é coincidência.
Vejamos primeiro este conceito aplicado em listas, ou seja, list comprehension.
Em essência um list comprehension é uma expressão onde aplicamos uma função qualquer em cada elemento de uma lista dada, colocando o resultado numa nova lista que será o valor retornado pela expressão.
Exemplos explicam melhor:
In [157]:
L = [1, 7, 3, 11]                   # quero uma lista onde cada elemento de L foi multiplicado por 3
l = [ele * 3 for ele in L]          # a expressão na direita da igualdade é o list comprehension desejado
l
Out[157]:
[3, 21, 9, 33]
No exemplo acima ele é uma variável, o nome não importa, poderia ser x ou elemento ou qualquer outro. O comprehension é a expressão entre chaves no lado direito. Uma lista, portanto.
O primeiro termo ele * 3 é a aplicação de uma função (no caso "multiplique por 3" mas pode ser qualquer coisa) num elemento genérico ele.
E que elemento genérico é este? Isto é respondido pelas palavras reservadas for e in, ou seja "...para cada ele em ...".
Em o que? Na lista L, o último termo desta expressão.

Podemos colocar restrições adicionais na lista sendo percorrida. Digamos que temos uma lista de números e queremos extrair dela uma lista dos que são números pares.
Para saber se um número é par basta ver qual resto dá se dividido por 2. Se o resto for zero o número é par. Em Python a expressão é n % 2 onde n é o número investigado.
Uma expressão equivalente a pergunta "o número n é par?" poderia ser assim: n % 2 == 0, que é verdadeira ou é falsa para um dado n.
Vejamos em Python:
In [158]:
L = [33, 776, 12, 19, 5, 7]
[x for x in L if x%2 == 0]       # lista dos x em L se (tais que) x é um número par
Out[158]:
[776, 12]
Estas expressões também podem ser usadas com dicionários (dict) e com conjuntos (set). Vejamos exemplos de dict comprehensions, que são no mesmo espírito de listas:
In [159]:
print(dic)                   # imprime o dicionario, pares chave-valor
{1: 'azul', 2: 'amarelo', 3: 'verde', 4: 'vermelho', 5: 'laranja', 6: 'preto', 7: 'branco'}
In [160]:
chave_par = {par for par in dic.keys() if par%2 == 0}     # percorre as chaves em dic.keys()
chave_par
Out[160]:
{2, 4, 6}
In [161]:
dic[6] = 'azul'                 # muda valor na chave 6 para 'azul'
dic
Out[161]:
{1: 'azul',
 2: 'amarelo',
 3: 'verde',
 4: 'vermelho',
 5: 'laranja',
 6: 'azul',
 7: 'branco'}
In [162]:
valor_azul = {item for item in dic.items() if item[1] == 'azul'}  # extrai os itens com valor 'azul'
valor_azul                # retorna um dicionario novo
Out[162]:
{(1, 'azul'), (6, 'azul')}
Vejamos exemplos de set comprehensions:
In [163]:
A = {1, 2, 'banana', 3, 4, 5}           # conjunto A
B = {'prata', 2, 7, 4, 8, 1}               # conjunto B
# quero criar um conjunto com tudo de A que não está em B
C = {x for x in A if not x in B}
C
Out[163]:
{'banana', 3, 5}

Só de distração, vamos inverter chaves e valores de nosso dicionário exemplo tmp. O resultado sera um novo dicionário.
Parece complicado ou laborioso? Não é, se usarmos lambda expressões, isto é, comprehensions:
In [164]:
print(tmp)
{1: 'a', 2: 'b', 3: 'c'}
In [165]:
{valor:chave for chave, valor in tmp.items()}
Out[165]:
{'a': 1, 'b': 2, 'c': 3}
How cool is that, heh?

No comments:

Post a Comment