O autor selecionou a COVID-19 Relief Fund para receber uma doação como parte do programa Write for DOnations.
O Python 3 possui uma série de estruturas de dados integrados, incluindo tuplas, dicionários e listas. As estruturas de dados nos fornecem uma maneira de organizar e armazenar dados. O módulo collections
(coleções) nos ajuda a preencher e manipular eficientemente as estruturas de dados.
Neste tutorial, vamos abordar três classes no módulo collections
para ajudá-lo a trabalhar com tuplas, dicionários e listas. Usaremos o namedtuples
para criar tuplas com campos nomeados, defaultdict
para agrupar informações de forma concisa em dicionários e deque
para adicionar com eficiência elementos a qualquer um dos lados de um objeto do tipo lista.
Para este tutorial, trabalharemos principalmente com um inventário de peixes que precisaremos modificar à medida que peixes são adicionados a ou removidos de um aquário fictício.
Para aproveitar ao máximo este tutorial, é recomendável ter alguma familiaridade com os tipos de dados tupla, dicionário e lista. Tanto com suas sintaxes, quanto com como recuperar dados deles. Você pode revisar estes tutoriais para as informações básicas necessárias:
As tuplas em Python são uma sequência ordenada imutável, ou inalterável, de elementos. As tuplas são frequentemente usadas para representar dados colunares. Por exemplo, linhas de um arquivo CSV ou linhas de um banco de dados SQL. Um aquário pode acompanhar seu inventário de peixes como uma série de tuplas.
Uma tupla de peixe individual:
("Sammy", "shark", "tank-a")
Esta tupla é composta por três elementos string.
Embora seja útil de algumas maneiras, esta tupla não indica com clareza o que cada um de seus campos representa. Na verdade, o elemento 0
é um nome, o elemento 1
é uma espécie e o elemento 2
é o tanque onde está localizado.
Explicação dos campos da tupla de peixe:
nome | espécie | tanque |
---|---|---|
Sammy | tubarão | tanque-a |
Essa tabela deixa claro que cada um dos três elementos da tupla possui um significado claro.
O namedtuple
do módulo collections
permite que você adicione nomes explícitos a cada elemento de uma tupla para tornar seus significados claros em seu programa Python.
Vamos usar o namedtuple
para gerar uma classe que nomeia com clareza cada elemento da tupla de peixe:
from collections import namedtuple
Fish = namedtuple("Fish", ["name", "species", "tank"])
from collections import namedtuple
dá ao seu programa Python acesso à função de fábrica namedtuple
. A chamada de função namedtuple()
retorna uma classe que está ligada ao nome Fish
(peixe). A função namedtuple()
possui dois argumentos: o nome desejado da nossa nova classe "Fish"
e uma lista de elementos nomeados ["name", "species", "tank"]
(“nome”, “espécie”, “tanque”).
Podemos usar a classe Fish
para representar a tupla de peixe de antes:
sammy = Fish("Sammy", "shark", "tank-a")
print(sammy)
Se executarmos esse código, veremos o seguinte resultado:
OutputFish(name='Sammy', species='shark', tank='tank-a')
A sammy
é instanciada usando a classe Fish
. sammy
é uma tupla com três elementos claramente nomeados.
Os campos de sammy
podem ser acessados pelo seu nome ou com um índice tradicional de tupla:
print(sammy.species)
print(sammy[1])
Se executarmos essas duas chamadas de print
, veremos o seguinte resultado:
Outputshark
shark
Acessar .species
retorna o mesmo valor que quando acessa-se o segundo elemento de sammy
usando [1]
.
Usar o namedtuple
do módulo collections
torna seu programa mais legível, ao mesmo tempo em que mantém as propriedades importantes de uma tupla (que são imutáveis e ordenadas).
Além disso, a função de fábrica namedtuple
adiciona diversos métodos extras para as instâncias de Fish
.
Use ._asdict()
para converter uma instância em um dicionário:
print(sammy._asdict())
Se executarmos print
, você verá um resultado como o seguinte:
Output{'name': 'Sammy', 'species': 'shark', 'tank': 'tank-a'}
Chamar .asdict()
em sammy
retorna um dicionário que mapeia cada um dos três nomes de campo aos seus valores correspondentes.
As versões do Python mais antigas que 3.8 podem gerar esta linha ligeiramente diferente como resultado. Você pode, por exemplo, ver um OrderedDict
em vez do dicionário evidente mostrado aqui.
Nota: em Python, os métodos com underline à esquerda são geralmente considerados “privados”. Métodos adicionais disponibilizados pelo namedtuple
(como _asdict()
, ._make()
, ._replace()
, etc), no entanto, são públicos.
Muitas vezes, pode ser útil coletar dados em dicionários Python. O defaultdict
do módulo collections
pode nos ajudar a reunir as informações em dicionários de maneira rápida e concisa.
O defaultdict
nunca provoca um KeyError
. Se uma chave não estiver presente, o defaultdict
simplesmente insere e retorna um valor de espaço reservado:
from collections import defaultdict
my_defaultdict = defaultdict(list)
print(my_defaultdict["missing"])
Se executarmos esse código, veremos um resultado como o seguinte:
Output[]
O defaultdict
insere e retorna um valor de espaço reservado ao invés de lançar um KeyError
. Neste caso, especificamos o valor de espaço reservado como uma lista.
Os dicionários regulares, por outro lado, lançarão um KeyError
para chaves que estejam faltando:
my_regular_dict = {}
my_regular_dict["missing"]
Se executarmos esse código, veremos um resultado como o seguinte:
OutputTraceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'missing'
O dicionário regular my_regular_dict
provoca um KeyError
quando tentamos acessar uma chave que não está presente.
O defaultdict
comporta-se de maneira diferente de um dicionário regular. Em vez de criar um KeyError
para uma chave que esteja faltando, o defaultdict
chama o valor de espaço reservado com nenhum argumento para criar um novo objeto. Neste caso, o list()
para criar uma lista vazia.
Voltando ao nosso exemplo de aquário fictício, vamos supor que temos uma lista de tuplas de peixe representando o inventário de um aquário.
fish_inventory = [
("Sammy", "shark", "tank-a"),
("Jamie", "cuttlefish", "tank-b"),
("Mary", "squid", "tank-a"),
]
Existem três peixes no aquário — seus nomes, espécies e tanques são anotados nestas três tuplas.
Nosso objetivo é organizar nosso inventário por tanque — queremos saber a lista de peixes presentes em cada tanque. Em outras palavras, queremos um dicionário que mapeie "tank-a"
para ["Jamie, "Mary"]
, e "tank-b"
para ["Jamie"]
.
Podemos usar o defaultdict
para agrupar peixes por tanque:
from collections import defaultdict
fish_inventory = [
("Sammy", "shark", "tank-a"),
("Jamie", "cuttlefish", "tank-b"),
("Mary", "squid", "tank-a"),
]
fish_names_by_tank = defaultdict(list)
for name, species, tank in fish_inventory:
fish_names_by_tank[tank].append(name)
print(fish_names_by_tank)
Ao executarmos esse código, veremos o seguinte resultado:
Outputdefaultdict(<class 'list'>, {'tank-a': ['Sammy', 'Mary'], 'tank-b': ['Jamie']})
O fish_names_by_tank
é declarado como um defaultdict
, que utiliza-se do padrão para inserir o list()
ao invés de lançar um KeyError
. Como isso garante que todas as chaves em fish_names_by_tank
apontarão para uma list
(lista), temos a liberdade para chamar o .append()
para adicionar nomes à lista de cada tanque.
O defaultdict
é útil aqui porque isso reduz as chances de KeyErrors
inesperados. A redução dos KeyErrors
inesperados significa que seu programa pode ser escrito com maior clareza e com menos linhas. Mais concretamente, o idioma defaultdict
permite que você evite instanciar manualmente uma lista vazia para cada tanque.
Sem o defaultdict
, o loop for
poderia ter ficado parecido com isto:
...
fish_names_by_tank = {}
for name, species, tank in fish_inventory:
if tank not in fish_names_by_tank:
fish_names_by_tank[tank] = []
fish_names_by_tank[tank].append(name)
O uso de apenas um dicionário regular (em vez de um defaultdict
) significa que o loop for
precisa sempre verificar se o tank
indicado em fish_names_by_tank
existe. Só depois de termos verificado que o tank
já está presente em fish_names_by_tank
, ou que acabou de ser inicializado com um []
, é que podemos acrescentar o nome do peixe.
O defaultdict
pode ajudar a diminuir o código boilerplate ao preencher os dicionários, pois ele nunca causa um KeyError
.
As listas Python são uma sequência ordenada mutável, ou alterável, de elementos. O Python pode acrescentar elementos às listas constantemente (o comprimento da lista não tem efeito no tempo necessário para a inserção). Entretanto, inserir elementos no início de uma lista pode demorar mais — o tempo necessário aumenta à medida que a lista aumenta de tamanho.
Em termos de notação O-grande, acrescentar um elemento a uma lista é uma operação de tempo constante O(1)
. A inserção de um elemento no início de uma lista, por outro lado, é mais lenta com um desempenho O(n)
.
Nota: os engenheiros de softwares geralmente medem o desempenho de procedimentos usando algo conhecido como notação “O-grande”. Quando o tamanho de uma entrada não tem efeito no tempo necessário para se executar um procedimento, diz-se que a execução ocorre em tempo constante, ou O(1)
(“O-grande de 1”). Assim como você aprendeu acima, o Python pode acrescentar elementos a listas com um desempenho temporal constante, também conhecido como O(1)
.
Às vezes, o tamanho de uma entrada afeta diretamente o tempo necessário para se executar um procedimento. A inserção de um elemento no início de uma lista Python, por exemplo, é executada mais lentamente à medida que existam mais elementos na lista. A notação O-grande utiliza a letra n
para representar o tamanho da entrada. Isso significa que a adição de itens no início de uma lista Python é executada em “tempo linear” ou O(n)
(“O-grande de n”).
De um modo geral, os procedimentos O(1)
são mais rápidos que os procedimentos O(n)
.
Somos capazes inserir elementos no início de uma lista Python:
favorite_fish_list = ["Sammy", "Jamie", "Mary"]
# O(n) performance
favorite_fish_list.insert(0, "Alice")
print(favorite_fish_list)
Se executarmos isso, veremos um resultado como o seguinte:
Output['Alice', 'Sammy', 'Jamie', 'Mary']
O método .insert(index, object)
em lista permite que adicionemos "Alice"
no início de favorite_fish_list
. No entanto, deve-se notar que a inserção de um elemento no início de uma lista possui um desempenho O(n)
. À medida que o comprimento de favorite_fish_list
cresce, o tempo necessário para inserir um peixe no início da lista crescerá proporcionalmente e demorará mais tempo.
O deque
(pronunciado como “deck”) do módulo collections
é um objeto do tipo lista que nos permite inserir elementos no início ou final de uma sequência com performance temporal constante (O(1)
).
Insira um item no início de um deque
:
from collections import deque
favorite_fish_deque = deque(["Sammy", "Jamie", "Mary"])
# O(1) performance
favorite_fish_deque.appendleft("Alice")
print(favorite_fish_deque)
Ao executarmos esse código, veremos o seguinte resultado:
Outputdeque(['Alice', 'Sammy', 'Jamie', 'Mary'])
Podemos instanciar um deque
usando uma coleção preexistente de elementos, neste caso, uma lista de três nomes de peixes favoritos. Chamar o método appendleft
de favorite_fish_deque
nos permite inserir um item no início de nossa coleção com o desempenho O(1)
. Ter um desempenho O(1)
significa que o tempo necessário para adicionar um item no início de favorite_fish_deque
não aumentará, mesmo se favorite_fish_deque
possuir milhares ou milhões de elementos.
Nota: embora o deque
adicione entradas no início de uma sequência mais eficientemente que uma lista, o deque
não realiza todas as suas operações com maior eficiência que uma lista. Por exemplo, acessar um item aleatório em um deque
possui um desempenho O(n)
, mas acessar um item aleatório em uma lista possui um desempenho O(1)
. Use o deque
quando for importante inserir ou remover elementos de um dos lados de sua coleção rapidamente. Uma comparação completa do desempenho temporal está disponível na wiki do Python.
O módulo collections
é uma parte poderosa da biblioteca padrão Python que permite que você trabalhe com dados de maneira concisa e eficiente. Este tutorial abordou três das classes disponibilizadas pelo módulo collections
, incluindo namedtuple
, defaultdict
e deque
.
A partir daqui, utilize a documentação do módulo collection
para aprender mais sobre outras classes e utilitários disponíveis. Para aprender mais sobre o Python em geral, consulte nossa série de tutoriais sobre Como programar em Python 3.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!