Iterando Dicionários Usando Loops 'For'

3139

Estou um pouco intrigado com o seguinte código:

d = {'x': 1, 'y': 2, 'z': 3} 
for key in d:
    print key, 'corresponds to', d[key]

O que eu não entendo é a keyparte. Como o Python reconhece que precisa apenas ler a chave do dicionário? Existe keyuma palavra especial em Python? Ou é simplesmente uma variável?

TopChef
fonte
4
Antes de postar uma nova resposta, considere que já existem mais de 10 respostas para esta pergunta. Por favor, verifique se sua resposta contribui com informações que não estão entre as existentes.
janniks 03/02

Respostas:

5394

key é apenas um nome de variável.

for key in d:

simplesmente fará um loop sobre as chaves no dicionário, em vez das chaves e valores. Para fazer um loop sobre a chave e o valor, você pode usar o seguinte:

Para Python 3.x:

for key, value in d.items():

Para Python 2.x:

for key, value in d.iteritems():

Para testar você mesmo, altere a palavra keypara poop.

No Python 3.x, iteritems()foi substituído por simply items(), que retorna uma visão de conjunto apoiada pelo dict, como iteritems()ainda melhor. Isto também está disponível em 2.7 as viewitems().

A operação items()funcionará para 2 e 3, mas em 2 retornará uma lista dos (key, value)pares do dicionário , que não refletirão alterações no ditado que ocorrem após a items()chamada. Se você deseja o comportamento 2.x no 3.x, pode ligar list(d.items()).

sberry
fonte
158
Adicionar um motivo negligenciado para não acessar um valor como este: d [chave] dentro do loop for faz com que a chave seja hash novamente (para obter o valor). Quando o dicionário é grande, esse hash extra será adicionado ao tempo total. Isso é discutido em Raymond Hettinger Tech Talk youtube.com/watch?v=anrOzOapJ2E
quiet_penguin
27
Pode fazer sentido mencionar que os itens serão iterados em ordem imprevisível e sortedsão necessários para estabilizá-lo.
yugr
5
@HarisankarKrishnaSwamy qual é a alternativa?
JoeyC
3
@yugr Por que você diz isso? Os documentos dizem Keys and values are iterated over in insertion order. [ docs.python.org/3/library/…
Geza Turi
4
@yugr No Python 3.7, os dicionários são ordenados por inserção e esse é um recurso de linguagem. Veja stackoverflow.com/a/39980744/9428564
Aimery
433

Não é que chave seja uma palavra especial, mas que os dicionários implementam o protocolo do iterador. Você pode fazer isso em sua classe, por exemplo, veja esta pergunta sobre como criar iteradores de classe.

No caso de dicionários, é implementado no nível C. Os detalhes estão disponíveis no PEP 234 . Em particular, a seção intitulada "Dicionário Iteradores":

  • Os dicionários implementam um slot tp_iter que retorna um iterador eficiente que itera sobre as chaves do dicionário. [...] Isso significa que podemos escrever

    for k in dict: ...

    que é equivalente a, mas muito mais rápido que

    for k in dict.keys(): ...

    desde que a restrição de modificações no dicionário (pelo loop ou por outro thread) não seja violada.

  • Adicione métodos a dicionários que retornam diferentes tipos de iteradores explicitamente:

    for key in dict.iterkeys(): ...
    
    for value in dict.itervalues(): ...
    
    for key, value in dict.iteritems(): ...

    Isso significa que for x in dicté uma abreviação de for x in dict.iterkeys().

Em Python 3, dict.iterkeys(), dict.itervalues()e dict.iteritems()não são mais suportados. Use e dict.keys(), em vez disso.dict.values()dict.items()

ars
fonte
207

A iteração sobre uma dictiteração através de suas chaves em nenhuma ordem específica, como você pode ver aqui:

Edit: (Este não é mais o caso do Python3.6 , mas observe que ainda não é um comportamento garantido )

>>> d = {'x': 1, 'y': 2, 'z': 3} 
>>> list(d)
['y', 'x', 'z']
>>> d.keys()
['y', 'x', 'z']

Para o seu exemplo, é uma idéia melhor usar dict.items():

>>> d.items()
[('y', 2), ('x', 1), ('z', 3)]

Isso fornece uma lista de tuplas. Quando você faz um loop sobre eles dessa maneira, cada tupla é descompactada ke vautomaticamente:

for k,v in d.items():
    print(k, 'corresponds to', v)

Usar ke vcomo nomes de variáveis ​​ao fazer um loop sobre um dicté bastante comum se o corpo do loop tiver apenas algumas linhas. Para loops mais complicados, pode ser uma boa ideia usar nomes mais descritivos:

for letter, number in d.items():
    print(letter, 'corresponds to', number)

É uma boa idéia adquirir o hábito de usar strings de formato:

for letter, number in d.items():
    print('{0} corresponds to {1}'.format(letter, number))
John La Rooy
fonte
11
Nas notas de versão do Python 3.7: "A natureza da preservação da ordem de inserção dos objetos dict agora é uma parte oficial das especificações da linguagem Python".
Gregory Arenius
86

key é simplesmente uma variável.

Para Python2.X :

d = {'x': 1, 'y': 2, 'z': 3} 
for my_var in d:
    print my_var, 'corresponds to', d[my_var]

... ou melhor,

d = {'x': 1, 'y': 2, 'z': 3} 
for the_key, the_value in d.iteritems():
    print the_key, 'corresponds to', the_value

Para Python3.X :

d = {'x': 1, 'y': 2, 'z': 3} 
for the_key, the_value in d.items():
    print(the_key, 'corresponds to', the_value)
ssoler
fonte
63

Quando você itera pelos dicionários usando a for .. in ..sintaxe-, sempre itera sobre as chaves (os valores são acessíveis usando dictionary[key]).

Para iterar sobre pares de valores-chave, use o Python 2 for k,v in s.iteritems()e o Python 3 for k,v in s.items().

Alexander Gessler
fonte
38
Observe que, para Python 3, é em items()vez deiteritems()
Andreas Fester 26/03
19

Iterando Dicionários Usando Loops 'For'

d = {'x': 1, 'y': 2, 'z': 3} 
for key in d:
    ...

Como o Python reconhece que precisa apenas ler a chave do dicionário? Key é uma palavra especial em Python? Ou é simplesmente uma variável?

Não são apenas forloops. A palavra importante aqui é "iterando".

Um dicionário é um mapeamento de chaves para valores:

d = {'x': 1, 'y': 2, 'z': 3} 

Sempre que iteramos sobre ele, iteramos sobre as chaves. O nome da variável keyé apenas para ser descritivo - e é bastante adequado para esse propósito.

Isso acontece na compreensão de uma lista:

>>> [k for k in d]
['x', 'y', 'z']

Isso acontece quando passamos o dicionário para a lista (ou qualquer outro objeto de tipo de coleção):

>>> list(d)
['x', 'y', 'z']

A maneira como o Python itera é, em um contexto em que precisa, chama o __iter__método do objeto (neste caso, o dicionário) que retorna um iterador (neste caso, um objeto keyiterator):

>>> d.__iter__()
<dict_keyiterator object at 0x7fb1747bee08>

Nós não devemos usar esses métodos especiais, em vez disso, use a respectiva função interna para chamá-la iter:

>>> key_iterator = iter(d)
>>> key_iterator
<dict_keyiterator object at 0x7fb172fa9188>

Os iteradores têm um __next__método - mas nós o chamamos com a função embutida next:

>>> next(key_iterator)
'x'
>>> next(key_iterator)
'y'
>>> next(key_iterator)
'z'
>>> next(key_iterator)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

Quando um iterador está esgotado, ele aumenta StopIteration. É assim que o Python sabe sair de um forloop, de uma compreensão de lista, de uma expressão geradora ou de qualquer outro contexto iterativo. StopIterationQuando um iterador aumenta, ele sempre será aumentado - se você quiser iterar novamente, precisará de um novo.

>>> list(key_iterator)
[]
>>> new_key_iterator = iter(d)
>>> list(new_key_iterator)
['x', 'y', 'z']

Voltando aos ditados

Já vimos ditados interagindo em muitos contextos. O que vimos é que sempre que iteramos sobre um ditado, obtemos as chaves. De volta ao exemplo original:

d = {'x': 1, 'y': 2, 'z': 3} 
for key in d:

Se mudarmos o nome da variável, ainda obteremos as chaves. Vamos tentar:

>>> for each_key in d:
...     print(each_key, '=>', d[each_key])
... 
x => 1
y => 2
z => 3

Se queremos iterar sobre os valores, precisamos usar o .valuesmétodo dict, ou para os dois juntos .items:

>>> list(d.values())
[1, 2, 3]
>>> list(d.items())
[('x', 1), ('y', 2), ('z', 3)]

No exemplo dado, seria mais eficiente iterar sobre os itens como este:

for a_key, corresponding_value in d.items():
    print(a_key, corresponding_value)

Mas, para fins acadêmicos, o exemplo da pergunta é ótimo.

Aaron Hall
fonte
17

Eu tenho um caso de uso em que tenho que percorrer o dict para obter a chave, par de valores e também o índice indicando onde estou. É assim que eu faço:

d = {'x': 1, 'y': 2, 'z': 3} 
for i, (key, value) in enumerate(d.items()):
   print(i, key, value)

Observe que, entre parênteses em torno da chave, o valor é importante; sem os parênteses, você obtém um ValueError "sem valores suficientes para descompactar".

jdhao
fonte
1
Como isso é relevante para a questão?
jorijnsmit 13/04
8

Você pode verificar a implementação do CPython dicttypeno GitHub. Esta é a assinatura do método que implementa o iterador dict:

_PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey,
             PyObject **pvalue, Py_hash_t *phash)

CPython dictobject.c

Ankur Agarwal
fonte
4

Para iterar sobre as teclas, é mais lento, mas melhor de usar my_dict.keys(). Se você tentou fazer algo assim:

for key in my_dict:
    my_dict[key+"-1"] = my_dict[key]-1

isso criaria um erro de tempo de execução porque você está alterando as chaves enquanto o programa está sendo executado. Se você está absolutamente decidido a reduzir o tempo, use o for key in my_dictcaminho, mas foi avisado;).

Neil Chowdhury o_O
fonte
2

Isso imprimirá a saída em ordem classificada por valores em ordem crescente.

d = {'x': 3, 'y': 1, 'z': 2}
def by_value(item):
    return item[1]

for key, value in sorted(d.items(), key=by_value):
    print(key, '->', value)

Resultado:

insira a descrição da imagem aqui

Amar Kumar
fonte