Introdução
Esta seção discute listas, dicionários e conjuntos.
Visão Geral
Programas frequentemente precisam trabalhar com muitos objetos.
- Um portfólio de ações
- Uma tabela de preços de ações
Existem três principais opções para usar.
- Listas (Lists). Dados ordenados.
- Dicionários (Dictionaries). Dados não ordenados.
- Conjuntos (Sets). Coleção não ordenada de itens únicos.
Listas como um Contêiner (Container)
Use uma lista quando a ordem dos dados for importante. Lembre-se que as listas podem conter qualquer tipo de objeto. Por exemplo, uma lista de tuplas.
portfolio = [
('GOOG', 100, 490.1),
('IBM', 50, 91.3),
('CAT', 150, 83.44)
]
portfolio[0] ## ('GOOG', 100, 490.1)
portfolio[2] ## ('CAT', 150, 83.44)
Construção de Listas (List construction)
Construindo uma lista do zero.
records = [] ## Lista inicial vazia
## Use .append() para adicionar mais itens
records.append(('GOOG', 100, 490.10))
records.append(('IBM', 50, 91.3))
...
Um exemplo ao ler registros de um arquivo.
records = [] ## Lista inicial vazia
with open('portfolio.csv', 'rt') as f:
next(f) ## Ignora o cabeçalho (header)
for line in f:
row = line.split(',')
records.append((row[0], int(row[1]), float(row[2])))
Dicionários (Dicts) como um Contêiner (Container)
Dicionários são úteis se você deseja pesquisas aleatórias rápidas (por nome da chave). Por exemplo, um dicionário de preços de ações:
prices = {
'GOOG': 513.25,
'CAT': 87.22,
'IBM': 93.37,
'MSFT': 44.12
}
Aqui estão algumas pesquisas simples:
>>> prices['IBM']
93.37
>>> prices['GOOG']
513.25
>>>
Construção de Dicionários (Dict Construction)
Exemplo de construção de um dicionário do zero.
prices = {} ## Dicionário inicial vazio
## Insere novos itens
prices['GOOG'] = 513.25
prices['CAT'] = 87.22
prices['IBM'] = 93.37
Um exemplo preenchendo o dicionário a partir do conteúdo de um arquivo.
prices = {} ## Dicionário inicial vazio
with open('prices.csv', 'rt') as f:
for line in f:
row = line.split(',')
prices[row[0]] = float(row[1])
Observação: Se você tentar isso no arquivo prices.csv, você descobrirá que quase funciona - há uma linha em branco no final que faz com que ele trave. Você precisará descobrir alguma maneira de modificar o código para levar isso em consideração (veja o Exercício 2.6).
Pesquisas em Dicionários (Dictionary Lookups)
Você pode testar a existência de uma chave.
if key in d:
## SIM
else:
## NÃO
Você pode pesquisar um valor que pode não existir e fornecer um valor padrão caso ele não exista.
name = d.get(key, default)
Um exemplo:
>>> prices.get('IBM', 0.0)
93.37
>>> prices.get('SCOX', 0.0)
0.0
>>>
Chaves Compostas (Composite keys)
Quase qualquer tipo de valor pode ser usado como uma chave de dicionário em Python. Uma chave de dicionário deve ser de um tipo que seja imutável. Por exemplo, tuplas:
holidays = {
(1, 1) : 'New Years',
(3, 14) : 'Pi day',
(9, 13) : "Programmer's day",
}
Então, para acessar:
>>> holidays[3, 14]
'Pi day'
>>>
Nem uma lista, nem um conjunto (set), nem outro dicionário podem servir como uma chave de dicionário, porque listas e dicionários são mutáveis.
Conjuntos (Sets)
Conjuntos são coleções de itens únicos não ordenados.
tech_stocks = { 'IBM','AAPL','MSFT' }
## Sintaxe alternativa
tech_stocks = set(['IBM', 'AAPL', 'MSFT'])
Conjuntos são úteis para testes de pertinência (membership tests).
>>> tech_stocks
set(['AAPL', 'IBM', 'MSFT'])
>>> 'IBM' in tech_stocks
True
>>> 'FB' in tech_stocks
False
>>>
Conjuntos também são úteis para eliminação de duplicatas.
names = ['IBM', 'AAPL', 'GOOG', 'IBM', 'GOOG', 'YHOO']
unique = set(names)
## unique = set(['IBM', 'AAPL','GOOG','YHOO'])
Operações adicionais em conjuntos:
unique.add('CAT') ## Adicionar um item
unique.remove('YHOO') ## Remover um item
s1 = { 'a', 'b', 'c'}
s2 = { 'c', 'd' }
s1 | s2 ## União de conjuntos (Set union) { 'a', 'b', 'c', 'd' }
s1 & s2 ## Intersecção de conjuntos (Set intersection) { 'c' }
s1 - s2 ## Diferença de conjuntos (Set difference) { 'a', 'b' }
Nestes exercícios, você começa a construir um dos principais programas usados para o restante deste curso. Faça seu trabalho no arquivo report.py.
Exercício 2.4: Uma lista de tuplas (tuples)
O arquivo portfolio.csv contém uma lista de ações em uma carteira. No Exercício 1.30, você escreveu uma função portfolio_cost(filename) que lia este arquivo e realizava um cálculo simples.
Seu código deveria ter algo parecido com isto:
## pcost.py
import csv
def portfolio_cost(filename):
'''Computa o custo total (ações*preço) de um arquivo de carteira'''
total_cost = 0.0
with open(filename, 'rt') as f:
rows = csv.reader(f)
headers = next(rows)
for row in rows:
nshares = int(row[1])
price = float(row[2])
total_cost += nshares * price
return total_cost
Usando este código como um guia aproximado, crie um novo arquivo report.py. Nesse arquivo, defina uma função read_portfolio(filename) que abre um determinado arquivo de carteira e o lê em uma lista de tuplas. Para fazer isso, você fará algumas pequenas modificações no código acima.
Primeiro, em vez de definir total_cost = 0, você criará uma variável que é inicialmente definida como uma lista vazia. Por exemplo:
portfolio = []
Em seguida, em vez de totalizar o custo, você transformará cada linha em uma tupla exatamente como fez no último exercício e a anexará a esta lista. Por exemplo:
for row in rows:
holding = (row[0], int(row[1]), float(row[2]))
portfolio.append(holding)
Finalmente, você retornará a lista portfolio resultante.
Experimente sua função interativamente (apenas um lembrete de que, para fazer isso, você primeiro precisa executar o programa report.py no interpretador):
Dica: Use -i ao executar o arquivo no terminal
>>> portfolio = read_portfolio('/home/labex/project/portfolio.csv')
>>> portfolio
[('AA', 100, 32.2), ('IBM', 50, 91.1), ('CAT', 150, 83.44), ('MSFT', 200, 51.23),
('GE', 95, 40.37), ('MSFT', 50, 65.1), ('IBM', 100, 70.44)]
>>>
>>> portfolio[0]
('AA', 100, 32.2)
>>> portfolio[1]
('IBM', 50, 91.1)
>>> portfolio[1][1]
50
>>> total = 0.0
>>> for s in portfolio:
total += s[1] * s[2]
>>> print(total)
44671.15
>>>
Esta lista de tuplas que você criou é muito semelhante a uma matriz 2-D. Por exemplo, você pode acessar uma coluna e linha específicas usando uma pesquisa como portfolio[row][column] onde row e column são inteiros.
Dito isso, você também pode reescrever o último loop for usando uma instrução como esta:
>>> total = 0.0
>>> for name, shares, price in portfolio:
total += shares*price
>>> print(total)
44671.15
>>>
Exercício 2.5: Lista de Dicionários (Dictionaries)
Pegue a função que você escreveu no Exercício 2.4 e modifique-a para representar cada ação na carteira com um dicionário em vez de uma tupla. Neste dicionário, use os nomes dos campos "name", "shares" e "price" para representar as diferentes colunas no arquivo de entrada.
Experimente esta nova função da mesma maneira que fez no Exercício 2.4.
>>> portfolio = read_portfolio('/home/labex/project/portfolio.csv')
>>> portfolio
[{'name': 'AA', 'shares': 100, 'price': 32.2}, {'name': 'IBM', 'shares': 50, 'price': 91.1},
{'name': 'CAT', 'shares': 150, 'price': 83.44}, {'name': 'MSFT', 'shares': 200, 'price': 51.23},
{'name': 'GE', 'shares': 95, 'price': 40.37}, {'name': 'MSFT', 'shares': 50, 'price': 65.1},
{'name': 'IBM', 'shares': 100, 'price': 70.44}]
>>> portfolio[0]
{'name': 'AA', 'shares': 100, 'price': 32.2}
>>> portfolio[1]
{'name': 'IBM', 'shares': 50, 'price': 91.1}
>>> portfolio[1]['shares']
50
>>> total = 0.0
>>> for s in portfolio:
total += s['shares']*s['price']
>>> print(total)
44671.15
>>>
Aqui, você notará que os diferentes campos para cada entrada são acessados por nomes de chaves em vez de números de colunas numéricos. Isso é frequentemente preferível porque o código resultante é mais fácil de ler posteriormente.
Visualizar grandes dicionários e listas pode ser confuso. Para limpar a saída para depuração, considere usar a função pprint.
>>> from pprint import pprint
>>> pprint(portfolio)
[{'name': 'AA', 'price': 32.2, 'shares': 100},
{'name': 'IBM', 'price': 91.1, 'shares': 50},
{'name': 'CAT', 'price': 83.44, 'shares': 150},
{'name': 'MSFT', 'price': 51.23, 'shares': 200},
{'name': 'GE', 'price': 40.37, 'shares': 95},
{'name': 'MSFT', 'price': 65.1, 'shares': 50},
{'name': 'IBM', 'price': 70.44, 'shares': 100}]
>>>
Exercício 2.6: Dicionários (Dictionaries) como um contêiner (container)
Um dicionário é uma maneira útil de acompanhar itens onde você deseja procurar itens usando um índice diferente de um inteiro. No shell do Python, tente brincar com um dicionário:
>>> prices = { }
>>> prices['IBM'] = 92.45
>>> prices['MSFT'] = 45.12
>>> prices
... veja o resultado ...
>>> prices['IBM']
92.45
>>> prices['AAPL']
... veja o resultado ...
>>> 'AAPL' in prices
False
>>>
O arquivo prices.csv contém uma série de linhas com preços de ações. O arquivo se parece com isto:
"AA",9.22
"AXP",24.85
"BA",44.85
"BAC",11.27
"C",3.72
...
Escreva uma função read_prices(filename) que lê um conjunto de preços como este em um dicionário onde as chaves do dicionário são os nomes das ações e os valores no dicionário são os preços das ações.
Para fazer isso, comece com um dicionário vazio e comece a inserir valores nele, assim como você fez acima. No entanto, você está lendo os valores de um arquivo agora.
Usaremos esta estrutura de dados para procurar rapidamente o preço de um determinado nome de ação.
Algumas pequenas dicas que você precisará para esta parte. Primeiro, certifique-se de usar o módulo csv como você fez antes --- não há necessidade de reinventar a roda aqui.
>>> import csv
>>> f = open('/home/labex/project/prices.csv', 'r')
>>> rows = csv.reader(f)
>>> for row in rows:
print(row)
['AA', '9.22']
['AXP', '24.85']
...
[]
>>>
A outra pequena complicação é que o arquivo prices.csv pode ter algumas linhas em branco nele. Observe como a última linha de dados acima é uma lista vazia --- significando que nenhum dado estava presente nessa linha.
Existe a possibilidade de que isso possa fazer com que seu programa morra com uma exceção. Use as instruções try e except para capturar isso conforme apropriado. Pensamento: seria melhor proteger contra dados ruins com uma instrução if em vez disso?
Depois de escrever sua função read_prices(), teste-a interativamente para garantir que funcione:
>>> prices = read_prices('/home/labex/project/prices.csv')
>>> prices['IBM']
106.28
>>> prices['MSFT']
20.89
>>>
Exercício 2.7: Descobrindo se você pode se aposentar
Junte todo este trabalho adicionando algumas instruções adicionais ao seu programa report.py que calcula ganho/perda. Essas instruções devem pegar a lista de ações no Exercício 2.5 e o dicionário de preços no Exercício 2.6 e calcular o valor atual da carteira, juntamente com o ganho/perda.
Resumo
Parabéns! Você concluiu o laboratório de Contêineres (Containers). Você pode praticar mais laboratórios no LabEx para aprimorar suas habilidades.