5. Estruturas de dados¶
Esse capítulo descreve algumas coisas que você já aprendeu em detalhes e adiciona algumas coisas novas também.
5.1. Mais sobre listas¶
O tipo de dado lista tem ainda mais métodos. Aqui estão apresentados todos os métodos de objetos do tipo lista:
-
list.
append
(x) Add an item to the end of the list; equivalent to
a[len(a):] = [x]
.
-
list.
extend
(L) Extend the list by appending all the items in the given list; equivalent to
a[len(a):] = L
.
-
list.
insert
(i, x) Insere um item em uma dada posição. O primeiro argumento é o índice do elemento antes do qual será feita a inserção, assim
a.insert(0, x)
insere um elemento na frente da lista ea.insert(len(a), x)
e equivale aa.append(x)
.
-
list.
remove
(x) Remove the first item from the list whose value is x. It is an error if there is no such item.
-
list.
pop
([i]) Remove um item em uma dada posição na lista e o retorna. Se nenhum índice é especificado,
a.pop()
remove e devolve o último item da lista. (Os colchetes ao redor do i na demonstração do método indica que o parâmetro é opcional, e não que é necessário escrever estes colchetes ao chamar o método. Você verá este tipo de notação frequentemente na Biblioteca de Referência Python.)
-
list.
index
(x) Return the index in the list of the first item whose value is x. It is an error if there is no such item.
-
list.
count
(x) Devolve o número de vezes em que x aparece na lista.
-
list.
sort
(cmp=None, key=None, reverse=False) Ordena os itens na lista (os argumentos podem ser usados para personalizar a ordenação, veja a função
sorted()
para maiores explicações).
-
list.
reverse
() Reverse the elements of the list, in place.
Um exemplo que usa a maior parte dos métodos das listas:
>>> a = [66.25, 333, 333, 1, 1234.5]
>>> print a.count(333), a.count(66.25), a.count('x')
2 1 0
>>> a.insert(2, -1)
>>> a.append(333)
>>> a
[66.25, 333, -1, 333, 1, 1234.5, 333]
>>> a.index(333)
1
>>> a.remove(333)
>>> a
[66.25, -1, 333, 1, 1234.5, 333]
>>> a.reverse()
>>> a
[333, 1234.5, 1, 333, -1, 66.25]
>>> a.sort()
>>> a
[-1, 1, 66.25, 333, 333, 1234.5]
>>> a.pop()
1234.5
>>> a
[-1, 1, 66.25, 333, 333]
You might have noticed that methods like insert
, remove
or sort
that
only modify the list have no return value printed – they return the default
None
. This is a design principle for all mutable data structures in
Python.
5.1.1. Usando listas como pilhas¶
Os métodos de lista tornam muito fácil utilizar listas como pilhas, onde o item adicionado por último é o primeiro a ser recuperado (política “último a entrar, primeiro a sair”). Para adicionar um item ao topo da pilha, use append()
. Para recuperar um item do topo da pilha use pop()
sem nenhum índice. Por exemplo:
>>> stack = [3, 4, 5]
>>> stack.append(6)
>>> stack.append(7)
>>> stack
[3, 4, 5, 6, 7]
>>> stack.pop()
7
>>> stack
[3, 4, 5, 6]
>>> stack.pop()
6
>>> stack.pop()
5
>>> stack
[3, 4]
5.1.2. Usando listas como filas¶
Você também pode usar uma lista como uma fila, onde o primeiro item adicionado é o primeiro a ser recuperado (política “primeiro a entrar, primeiro a sair”); porém, listas não são eficientes para esta finalidade. Embora appends e pops no final da lista sejam rápidos, fazer inserts ou pops no início da lista é lento (porque todos os demais elementos têm que ser deslocados).
Para implementar uma fila, use a classe collections.deque
que foi projetada para permitir appends e pops eficientes nas duas extremidades. Por exemplo:
>>> from collections import deque
>>> queue = deque(["Eric", "John", "Michael"])
>>> queue.append("Terry") # Terry arrives
>>> queue.append("Graham") # Graham arrives
>>> queue.popleft() # The first to arrive now leaves
'Eric'
>>> queue.popleft() # The second to arrive now leaves
'John'
>>> queue # Remaining queue in order of arrival
deque(['Michael', 'Terry', 'Graham'])
5.1.3. Functional Programming Tools¶
There are three built-in functions that are very useful when used with lists:
filter()
, map()
, and reduce()
.
filter(function, sequence)
returns a sequence consisting of those items from
the sequence for which function(item)
is true. If sequence is a
str
, unicode
or tuple
, the result will be of the
same type; otherwise, it is always a list
. For example, to compute a
sequence of numbers divisible by 3 or 5:
>>> def f(x): return x % 3 == 0 or x % 5 == 0
...
>>> filter(f, range(2, 25))
[3, 5, 6, 9, 10, 12, 15, 18, 20, 21, 24]
map(function, sequence)
calls function(item)
for each of the sequence’s
items and returns a list of the return values. For example, to compute some
cubes:
>>> def cube(x): return x*x*x
...
>>> map(cube, range(1, 11))
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
More than one sequence may be passed; the function must then have as many
arguments as there are sequences and is called with the corresponding item from
each sequence (or None
if some sequence is shorter than another). For
example:
>>> seq = range(8)
>>> def add(x, y): return x+y
...
>>> map(add, seq, seq)
[0, 2, 4, 6, 8, 10, 12, 14]
reduce(function, sequence)
returns a single value constructed by calling the
binary function function on the first two items of the sequence, then on the
result and the next item, and so on. For example, to compute the sum of the
numbers 1 through 10:
>>> def add(x,y): return x+y
...
>>> reduce(add, range(1, 11))
55
If there’s only one item in the sequence, its value is returned; if the sequence is empty, an exception is raised.
A third argument can be passed to indicate the starting value. In this case the starting value is returned for an empty sequence, and the function is first applied to the starting value and the first sequence item, then to the result and the next item, and so on. For example,
>>> def sum(seq):
... def add(x,y): return x+y
... return reduce(add, seq, 0)
...
>>> sum(range(1, 11))
55
>>> sum([])
0
Don’t use this example’s definition of sum()
: since summing numbers is
such a common need, a built-in function sum(sequence)
is already provided,
and works exactly like this.
5.1.4. Compreensões de lista¶
Compreensões de lista fornece uma maneira concisa de criar uma lista. Aplicações comuns são criar novas listas onde cada elemento é o resultado de alguma operação aplicada a cada elemento de outra sequência ou iterável, ou criar uma subsequência de elementos que satisfaçam uma certa condição. (N.d.T. o termo original em inglês é list comprehensions, muito utilizado no Brasil; também se usa a abreviação listcomp).
Por exemplo, suponha que queremos criar uma lista de quadrados, assim:
>>> squares = []
>>> for x in range(10):
... squares.append(x**2)
...
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
We can obtain the same result with:
squares = [x**2 for x in range(10)]
This is also equivalent to squares = map(lambda x: x**2, range(10))
,
but it’s more concise and readable.
A list comprehension consists of brackets containing an expression followed
by a for
clause, then zero or more for
or if
clauses. The result will be a new list resulting from evaluating the expression
in the context of the for
and if
clauses which follow it.
For example, this listcomp combines the elements of two lists if they are not
equal:
>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
and it’s equivalent to:
>>> combs = []
>>> for x in [1,2,3]:
... for y in [3,1,4]:
... if x != y:
... combs.append((x, y))
...
>>> combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
Note como a ordem das instruções for
e if
é a mesma em ambos os trechos.
Se a expressão é uma tupla (ex., (x, y)
no exemplo anterior), ela deve ser inserida entre parênteses.
>>> vec = [-4, -2, 0, 2, 4]
>>> # create a new list with the values doubled
>>> [x*2 for x in vec]
[-8, -4, 0, 4, 8]
>>> # filter the list to exclude negative numbers
>>> [x for x in vec if x >= 0]
[0, 2, 4]
>>> # apply a function to all the elements
>>> [abs(x) for x in vec]
[4, 2, 0, 2, 4]
>>> # call a method on each element
>>> freshfruit = [' banana', ' loganberry ', 'passion fruit ']
>>> [weapon.strip() for weapon in freshfruit]
['banana', 'loganberry', 'passion fruit']
>>> # create a list of 2-tuples like (number, square)
>>> [(x, x**2) for x in range(6)]
[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
>>> # the tuple must be parenthesized, otherwise an error is raised
>>> [x, x**2 for x in range(6)]
File "<stdin>", line 1, in <module>
[x, x**2 for x in range(6)]
^
SyntaxError: invalid syntax
>>> # flatten a list using a listcomp with two 'for'
>>> vec = [[1,2,3], [4,5,6], [7,8,9]]
>>> [num for elem in vec for num in elem]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Compreensões de lista podem conter expressões complexas e funções aninhadas:
>>> from math import pi
>>> [str(round(pi, i)) for i in range(1, 6)]
['3.1', '3.14', '3.142', '3.1416', '3.14159']
5.1.4.1. Compreensões de lista aninhadas¶
A expressão inicial em uma list comprehension pode ser qualquer expressão arbitrária, incluindo outra list comprehension.
Observe este exemplo de uma matriz 3x4 implementada como uma lista de 3 listas de comprimento 4:
>>> matrix = [
... [1, 2, 3, 4],
... [5, 6, 7, 8],
... [9, 10, 11, 12],
... ]
A compreensão de lista abaixo transpõe as linhas e colunas:
>>> [[row[i] for row in matrix] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
Como vimos na seção anterior, a compreensão de lista aninhada é computada no contexto da cláusula for
seguinte, portanto o exemplo acima equivale a:
>>> transposed = []
>>> for i in range(4):
... transposed.append([row[i] for row in matrix])
...
>>> transposed
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
e isso, por sua vez, faz o mesmo que isto:
>>> transposed = []
>>> for i in range(4):
... # the following 3 lines implement the nested listcomp
... transposed_row = []
... for row in matrix:
... transposed_row.append(row[i])
... transposed.append(transposed_row)
...
>>> transposed
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
Na prática, você deve dar preferência a funções embutidas em vez de expressões complexas. A função zip()
resolve muito bem este caso de uso:
>>> zip(*matrix)
[(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]
Veja Desempacotando listas de argumentos para entender o uso do asterisco neste exemplo.
5.2. The del
statement¶
There is a way to remove an item from a list given its index instead of its
value: the del
statement. This differs from the pop()
method
which returns a value. The del
statement can also be used to remove
slices from a list or clear the entire list (which we did earlier by assignment
of an empty list to the slice). For example:
>>> a = [-1, 1, 66.25, 333, 333, 1234.5]
>>> del a[0]
>>> a
[1, 66.25, 333, 333, 1234.5]
>>> del a[2:4]
>>> a
[1, 66.25, 1234.5]
>>> del a[:]
>>> a
[]
del
também pode ser usado para remover totalmente uma variável:
>>> del a
Referenciar a variável a
depois de sua remoção constitui erro (pelo menos até que seja feita uma nova atribuição para ela). Encontraremos outros usos para a instrução del
mais tarde.
5.3. Tuplas e sequências¶
Vimos que listas e strings têm muitas propriedades em comum, como indexação e operações de fatiamento. Elas são dois exemplos de sequências (veja Sequence Types — str, unicode, list, tuple, bytearray, buffer, xrange). Como Python é uma linguagem em evolução, outros tipos de sequências podem ser adicionados. Existe ainda um outro tipo de sequência padrão na linguagem: a tupla.
Uma tupla consiste em uma sequência de valores separados por vírgulas, por exemplo:
>>> t = 12345, 54321, 'hello!'
>>> t[0]
12345
>>> t
(12345, 54321, 'hello!')
>>> # Tuples may be nested:
... u = t, (1, 2, 3, 4, 5)
>>> u
((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))
>>> # Tuples are immutable:
... t[0] = 88888
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> # but they can contain mutable objects:
... v = ([1, 2, 3], [3, 2, 1])
>>> v
([1, 2, 3], [3, 2, 1])
Como você pode ver no trecho acima, na saída do console as tuplas são sempre envolvidas por parênteses, assim tuplas aninhadas podem ser lidas corretamente. Na criação, tuplas podem ser envolvidas ou não por parênteses, desde que o contexto não exija os parênteses (como no caso da tupla dentro de uma expressão maior). Não é possível atribuir itens individuais de uma tupla, contudo é possível criar tuplas que contenham objetos mutáveis, como listas.
Apesar de tuplas serem similares a listas, elas são frequentemente utilizadas em situações diferentes e com propósitos distintos. Tuplas são imutáveis, e usualmente contém uma sequência heterogênea de elementos que são acessados via desempacotamento (ver a seguir nessa seção) ou índice (ou mesmo por um atributo no caso de namedtuples
). Listas são mutáveis, e seus elementos geralmente são homogêneos e são acessados iterando sobre a lista.
Um problema especial é a criação de tuplas contendo 0 ou 1 itens: a sintaxe usa certos truques para acomodar estes casos. Tuplas vazias são construídas por um par de parênteses vazios; uma tupla unitária é construída por um único valor e uma vírgula entre parênteses (não basta colocar um único valor entre parênteses). Feio, mas funciona. Por exemplo:
>>> empty = ()
>>> singleton = 'hello', # <-- note trailing comma
>>> len(empty)
0
>>> len(singleton)
1
>>> singleton
('hello',)
A instrução t = 12345, 54321, 'bom dia!'
é um exemplo de empacotamento de tupla: os valores 12345
, 54321
e 'bom dia!'
são empacotados em uma tupla. A operação inversa também é possível:
>>> x, y, z = t
This is called, appropriately enough, sequence unpacking and works for any sequence on the right-hand side. Sequence unpacking requires the list of variables on the left to have the same number of elements as the length of the sequence. Note that multiple assignment is really just a combination of tuple packing and sequence unpacking.
5.4. Conjuntos¶
Python também inclui um tipo de dados para conjuntos, chamado set
. Um conjunto é uma coleção desordenada de elementos, sem elementos repetidos. Usos comuns para conjuntos incluem a verificação eficiente da existência de objetos e a eliminação de itens duplicados. Conjuntos também suportam operações matemáticas como união, interseção, diferença e diferença simétrica.
Chaves ou a função set()
podem ser usados para criar conjuntos. Note: para criar um conjunto vazio você precisa usar set()
, não {}
; este último cria um dicionário vazio, uma estrutura de dados que discutiremos na próxima seção.
Uma pequena demonstração:
>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
>>> fruit = set(basket) # create a set without duplicates
>>> fruit
set(['orange', 'pear', 'apple', 'banana'])
>>> 'orange' in fruit # fast membership testing
True
>>> 'crabgrass' in fruit
False
>>> # Demonstrate set operations on unique letters from two words
...
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a # unique letters in a
set(['a', 'r', 'b', 'c', 'd'])
>>> a - b # letters in a but not in b
set(['r', 'd', 'b'])
>>> a | b # letters in either a or b
set(['a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'])
>>> a & b # letters in both a and b
set(['a', 'c'])
>>> a ^ b # letters in a or b but not both
set(['r', 'd', 'b', 'm', 'z', 'l'])
Da mesma forma que compreensão de listas, compreensões de conjunto também são suportadas:
>>> a = {x for x in 'abracadabra' if x not in 'abc'}
>>> a
set(['r', 'd'])
5.5. Dicionários¶
Outra estrutura de dados muito útil embutida em Python é o dicionário, cujo tipo é dict
(ver Tipo de Mapeamento — dict). Dicionários são também chamados de “memória associativa” ou “vetor associativo” em outras linguagens. Diferente de sequências que são indexadas por inteiros, dicionários são indexados por chaves (keys), que podem ser de qualquer tipo imutável (como strings e inteiros). Tuplas também podem ser chaves se contiverem apenas strings, inteiros ou outras tuplas. Se a tupla contiver, direta ou indiretamente, qualquer valor mutável, não poderá ser chave. Listas não podem ser usadas como chaves porque podem ser modificadas internamente pela atribuição em índices ou fatias, e por métodos como append()
e extend()
.
It is best to think of a dictionary as an unordered set of key: value pairs,
with the requirement that the keys are unique (within one dictionary). A pair of
braces creates an empty dictionary: {}
. Placing a comma-separated list of
key:value pairs within the braces adds initial key:value pairs to the
dictionary; this is also the way dictionaries are written on output.
As principais operações em um dicionário são armazenar e recuperar valores a partir de chaves. Também é possível remover um par chave:valor com o comando del
. Se você armazenar um valor utilizando uma chave já presente, o antigo valor será substituído pelo novo. Se tentar recuperar um valor usando uma chave inexistente, será gerado um erro.
The keys()
method of a dictionary object returns a list of all the keys
used in the dictionary, in arbitrary order (if you want it sorted, just apply
the sorted()
function to it). To check whether a single key is in the
dictionary, use the in
keyword.
A seguir, um exemplo de uso do dicionário:
>>> tel = {'jack': 4098, 'sape': 4139}
>>> tel['guido'] = 4127
>>> tel
{'sape': 4139, 'guido': 4127, 'jack': 4098}
>>> tel['jack']
4098
>>> del tel['sape']
>>> tel['irv'] = 4127
>>> tel
{'guido': 4127, 'irv': 4127, 'jack': 4098}
>>> tel.keys()
['guido', 'irv', 'jack']
>>> 'guido' in tel
True
O construtor dict()
produz dicionários diretamente de sequências de pares chave-valor:
>>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
{'sape': 4139, 'jack': 4098, 'guido': 4127}
Além disso, as compreensões de dicionários podem ser usadas para criar dicionários a partir de expressões arbitrárias de chave e valor:
>>> {x: x**2 for x in (2, 4, 6)}
{2: 4, 4: 16, 6: 36}
Quando chaves são strings simples, é mais fácil especificar os pares usando argumentos nomeados no construtor:
>>> dict(sape=4139, guido=4127, jack=4098)
{'sape': 4139, 'jack': 4098, 'guido': 4127}
5.6. Técnicas de iteração¶
Ao iterar sobre sequências, a posição e o valor correspondente podem ser obtidos simultaneamente usando a função enumerate()
.
>>> for i, v in enumerate(['tic', 'tac', 'toe']):
... print i, v
...
0 tic
1 tac
2 toe
Para percorrer duas ou mais sequências ao mesmo tempo, as entradas podem ser pareadas com a função zip()
.
>>> questions = ['name', 'quest', 'favorite color']
>>> answers = ['lancelot', 'the holy grail', 'blue']
>>> for q, a in zip(questions, answers):
... print 'What is your {0}? It is {1}.'.format(q, a)
...
What is your name? It is lancelot.
What is your quest? It is the holy grail.
What is your favorite color? It is blue.
Para percorrer uma sequência em ordem inversa, chame a função reversed()
com a sequência na ordem original.
>>> for i in reversed(xrange(1,10,2)):
... print i
...
9
7
5
3
1
Para percorrer uma sequência de maneira ordenada, use a função sorted()
, que retorna uma lista ordenada com os itens, mantendo a sequência original inalterada.
>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
>>> for f in sorted(set(basket)):
... print f
...
apple
banana
orange
pear
When looping through dictionaries, the key and corresponding value can be
retrieved at the same time using the iteritems()
method.
>>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}
>>> for k, v in knights.iteritems():
... print k, v
...
gallahad the pure
robin the brave
Às vezes é tentador alterar uma lista enquanto você itera sobre ela; porém, costuma ser mais simples e seguro criar uma nova lista.
>>> import math
>>> raw_data = [56.2, float('NaN'), 51.7, 55.3, 52.5, float('NaN'), 47.8]
>>> filtered_data = []
>>> for value in raw_data:
... if not math.isnan(value):
... filtered_data.append(value)
...
>>> filtered_data
[56.2, 51.7, 55.3, 52.5, 47.8]
5.7. Mais sobre condições¶
As condições de controle usadas em while
e if
podem conter quaisquer operadores, não apenas comparações.
Os operadores de comparação in
e not in
verificam se um valor ocorre (ou não ocorre) em uma dada sequência. Os operadores is
e is not
comparam se dois objetos são na verdade o mesmo objeto; isto só é relevante no contexto de objetos mutáveis, como listas. Todos os operadores de comparação possuem a mesma precedência, que é menor do que a prioridade de todos os operadores numéricos.
Comparações podem ser encadeadas: Por exemplo a < b == c
testa se a
é menor que b
e também se b
é igual a c
.
Comparações podem ser combinadas através de operadores booleanos and
e or
, e o resultado de uma comparação (ou de qualquer outra expressão), pode ter seu valor booleano negado através de not
. Estes possuem menor prioridade que os demais operadores de comparação. Entre eles, not
é o de maior prioridade e or
o de menor. Dessa forma, a condição A and not B or C
é equivalente a (A and (not B)) or C
. Naturalmente, parênteses podem ser usados para expressar o agrupamento desejado.
Os operadores booleanos and
e or
são operadores curto-circuito: seus argumentos são avaliados da esquerda para a direita, e a avaliação encerra quando o resultado é determinado. Por exemplo, se A
e C
são expressões verdadeiras, mas B
é falsa, então A and B and C
não chega a avaliar a expressão C
. Em geral, quando usado sobre valores genéricos e não como booleanos, o valor do resultado de um operador curto-circuito é o último valor avaliado na expressão.
É possível atribuir o resultado de uma comparação ou outra expressão booleana para uma variável. Por exemplo:
>>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance'
>>> non_null = string1 or string2 or string3
>>> non_null
'Trondheim'
Note that in Python, unlike C, assignment cannot occur inside expressions. C
programmers may grumble about this, but it avoids a common class of problems
encountered in C programs: typing =
in an expression when ==
was
intended.
5.8. Comparando sequências e outros tipos¶
Sequence objects may be compared to other objects with the same sequence type. The comparison uses lexicographical ordering: first the first two items are compared, and if they differ this determines the outcome of the comparison; if they are equal, the next two items are compared, and so on, until either sequence is exhausted. If two items to be compared are themselves sequences of the same type, the lexicographical comparison is carried out recursively. If all items of two sequences compare equal, the sequences are considered equal. If one sequence is an initial sub-sequence of the other, the shorter sequence is the smaller (lesser) one. Lexicographical ordering for strings uses the ASCII ordering for individual characters. Some examples of comparisons between sequences of the same type:
(1, 2, 3) < (1, 2, 4)
[1, 2, 3] < [1, 2, 4]
'ABC' < 'C' < 'Pascal' < 'Python'
(1, 2, 3, 4) < (1, 2, 4)
(1, 2) < (1, 2, -1)
(1, 2, 3) == (1.0, 2.0, 3.0)
(1, 2, ('aa', 'ab')) < (1, 2, ('abc', 'a'), 4)
Note that comparing objects of different types is legal. The outcome is deterministic but arbitrary: the types are ordered by their name. Thus, a list is always smaller than a string, a string is always smaller than a tuple, etc. 1 Mixed numeric types are compared according to their numeric value, so 0 equals 0.0, etc.
Notas de rodapé
- 1
The rules for comparing objects of different types should not be relied upon; they may change in a future version of the language.