TypeError: el objeto 'dict_keys' no admite indexación

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]

Cuando ejecuto la shufflefunción, aparece el siguiente error, ¿por qué es eso?

TypeError: 'dict_keys' object does not support indexing
gate_007
fuente
77
parece ser un error de
python3

Respuestas:

231

Claramente estás pasando d.keys()a tu shufflefunción. Probablemente esto fue escrito con python2.x (cuando se d.keys()devuelve una lista). Con python3.x, d.keys()devuelve un dict_keysobjeto que se comporta mucho más como a setque a list. Como tal, no se puede indexar.

La solución es pasar list(d.keys())(o simplemente list(d)) a shuffle.

mgilson
fuente
22
. . . O simplemente, lo list(d)que le dará una lista de claves en python2.xy python3.x sin hacer ninguna copia :-)
mgilson
11
Esta es una extraña decisión de diseño de cambio de ruptura para python3.
Jason
9
Puede pensar que sí, pero definitivamente creo que fue la decisión correcta. El dict_keysobjeto se comporta mucho más como las teclas de la mitad de un dict. Específicamente, admiten pruebas de membresía O (1) (y otros métodos similares a conjuntos que se pueden implementar de manera eficiente además de ese hecho). Estas cosas no son posibles con una lista y si desea una lista de las claves del dict, siempre ha podido hacer list(your_dictionary)para obtenerla.
mgilson
Esto es útil para mí al ver que python3 requiere que envuelvamos el diccionario con la lista.
DataEngineer
2
@Crt: shufflees el nombre de la función en el código del póster original (la función que arroja el error). Al mirar el código, creo que fue copiado / pegado de random.shufflela implementación de la biblioteca estándar :-)
mgilson
11

Estás pasando el resultado de somedict.keys()a la función. En Python 3, dict.keysno devuelve una lista, pero un objeto tipo conjunto que representa una vista de las teclas del diccionario y (al ser tipo conjunto) no admite la indexación.

Para solucionar el problema, use list(somedict.keys())para recoger las claves y trabaje con eso.

usuario4815162342
fuente
10

Convertir un iterable en una lista puede tener un costo. En cambio, para obtener el primer elemento, puede usar:

next(iter(keys))

O, si desea iterar sobre todos los elementos, puede usar:

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

¿Por qué necesita implementar shuffle cuando ya existe? Mantente sobre los hombros de los 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
fuente
La respuesta es relevante para el conocimiento general, pero no se refiere a lo que preguntaba el OP.
JC Rocamonde
Tienes razón. Parece que quiere implementar su propio aleatorizador.
FooBar167
1
psah, tal vez en realidad no sabía que podía usar el incorporado, pero la pregunta en realidad parece ser sobre un error de tipo. Aún así, espero que haya cambiado y utilizado su opción (a menos que sea algo muy específico) para seguir los principios básicos de economía de código y DRY.
JC Rocamonde
1

En Python 2, dict.keys () devuelve una lista, mientras que en Python 3 devuelve un generador.

Solo podría iterar sobre sus valores; de lo contrario, tendrá que convertirlo explícitamente en una lista, es decir, pasarlo a una función de lista.

DeWil
fuente