TypeError: o objeto 'dict_keys' não suporta indexação

144
def shuffle(self, x, random=None, int=int):
    """x, random=random.random -> shuffle list x in place; return None.

    Optional arg random is a 0-argument function returning a random
    float in [0.0, 1.0); by default, the standard random.random.
    """

    randbelow = self._randbelow
    for i in reversed(range(1, len(x))):
        # pick an element in x[:i+1] with which to exchange x[i]
        j = randbelow(i+1) if random is None else int(random() * (i+1))
        x[i], x[j] = x[j], x[i]

Quando executo a shufflefunção, ela gera o seguinte erro, por que isso?

TypeError: 'dict_keys' object does not support indexing
gate_007
fonte
7
parece ser um erro
python3

Respostas:

231

Claramente você está passando para d.keys()a sua shufflefunção. Provavelmente isso foi escrito com python2.x (quando d.keys()retornou uma lista). Com python3.x, d.keys()retorna um dict_keysobjeto que se comporta muito mais como a setque com a list. Como tal, não pode ser indexado.

A solução é passar list(d.keys())(ou simplesmente list(d)) para shuffle.

mgilson
fonte
22
. . . Ou apenas list(d)o que lhe dará uma lista de chaves em ambos python2.x e python3.x sem fazer quaisquer cópias :-)
mgilson
11
Esta é uma decisão estranha no design de mudança de quebra para python3.
21417 Jason
9
Você pode pensar assim, mas eu definitivamente acho que foi a decisão correta. O dict_keysobjeto se comporta muito mais como apenas as teclas metade de um ditado. Especificamente, eles suportam O (1) teste de associação (e outros métodos de conjunto que podem ser implementados com eficiência além disso). Essas coisas não são possíveis com uma lista e, se você quiser uma lista das chaves do ditado, sempre poderá fazer isso list(your_dictionary)para obtê-lo.
mgilson
isso é útil para mim, ver que python3 exige que agrupemos o dicionário com a lista.
DataEngineer
2
@Crt - shuffleé o nome da função no código do pôster original (a função que está lançando o erro). Olhando para o código, acho que era copiar / colar de random.shuffleimplementação 's na biblioteca padrão :-)
mgilson
11

Você está passando o resultado de somedict.keys()para a função. No Python 3, dict.keysnão retorna uma lista, mas um objeto semelhante a um conjunto que representa uma visualização das chaves do dicionário e (sendo semelhante a um conjunto) não suporta indexação.

Para corrigir o problema, use list(somedict.keys())para coletar as chaves e trabalhe com isso.

user4815162342
fonte
10

Converter um iterável em uma lista pode ter um custo. Em vez disso, para obter o primeiro item, você pode usar:

next(iter(keys))

Ou, se você quiser iterar sobre todos os itens, poderá usar:

items = iter(keys)
while True:
    try:
        item = next(items)
    except StopIteration as e:
        pass # finish
sahama
fonte
1

Por que você precisa implementar o shuffle quando ele já existe? Fique nos ombros dos gigantes.

import random

d1 = {0:'zero', 1:'one', 2:'two', 3:'three', 4:'four',
     5:'five', 6:'six', 7:'seven', 8:'eight', 9:'nine'}

keys = list(d1)
random.shuffle(keys)

d2 = {}
for key in keys: d2[key] = d1[key]

print(d1)
print(d2)
FooBar167
fonte
A resposta é relevante para o conhecimento geral, mas não se refere ao que o OP estava perguntando.
JC Rocamonde
Você está certo. Parece que ele quer implementar seu próprio randomizador.
FooBar167
1
psah, talvez ele não soubesse que poderia usar o built-in, mas a pergunta realmente parece ser sobre um erro de tipo. Ainda assim, espero que ele tenha trocado e usado sua opção (a menos que seja algo muito específico) para seguir os princípios básicos de DRY e economia de código.
JC Rocamonde
1

No Python 2, dict.keys () retorna uma lista, enquanto no Python 3 retorna um gerador.

Você só pode iterar sobre seus valores; caso contrário, pode ser necessário convertê-lo explicitamente em uma lista, ou seja, passá-lo para uma função de lista.

DeWil
fonte