¿Cómo verificar si una variable es un diccionario en Python?

214

¿Cómo comprobarías si una variable es un diccionario en Python?

Por ejemplo, me gustaría que recorra los valores del diccionario hasta que encuentre un diccionario. Luego, recorre el que encuentra:

dict = {'abc': 'abc', 'def': {'ghi': 'ghi', 'jkl': 'jkl'}}
for k, v in dict.iteritems():
    if ###check if v is a dictionary:
        for k, v in v.iteritems():
            print(k, ' ', v)
    else:
        print(k, ' ', v)
Riley
fuente
También stackoverflow.com/questions/378927/… (que está marcado como un duplicado del anterior).
NPE
También stackoverflow.com/q/2225038/770830
disponible el
40
No, no es la misma pregunta. La respuesta a esta pregunta y a las otras preguntas enumeradas aquí contienen sustancialmente la misma información. Pero la respuesta a "Cómo verificar si una variable es un diccionario en python" es "Usar type () o isinstance ()", lo que lleva a una nueva pregunta, cuál es la diferencia entre type () y isinstance () . Pero la persona que hace la primera pregunta no puede saberlo hasta que se responda la primera pregunta. Ergo, diferentes preguntas, lo que importa cuando buscas tu pregunta en el sitio.
13
Estoy de acuerdo con @mbakeranalecta. Vine aquí buscando la respuesta a la pregunta "¿Cómo verificar si una variable es un diccionario en Python?" y nunca hubiera pensado mirar mi respuesta en "Diferencias entre isinstance () y type () en python".
lodebari
55
Para verificar si una variable es un diccionario en particular, probablemente debería usar isinstance(v, collections.abc.Mapping). En otras palabras, este no es un duplicado exacto de "Diferencias entre isinstance () y type ()".
Josh Kelley

Respuestas:

279

Podrías usar if type(ele) is dicto usar isinstance(ele, dict)cuál funcionaría si hubieras subclasificado dict:

d = {'abc':'abc','def':{'ghi':'ghi','jkl':'jkl'}}
for ele in d.values():
    if isinstance(ele,dict):
       for k, v in ele.items():
           print(k,' ',v)
Padraic Cunningham
fuente
70
I-abajo votado en esta respuesta porque la respuesta correcta a la pregunta general es: isinstance(ele, collections.Mapping). Funciona para dict(), collections.OrderedDict()y collections.UserDict(). El ejemplo en la pregunta es lo suficientemente específico para que funcione la respuesta de Padriac, pero no es lo suficientemente bueno para el caso general.
Alexander Ryzhov
44
Desestimé esta respuesta porque si vas a invocar, ele.items()¿por qué verificas el tipo? EAFP / pato tipificación trabaja aquí, sólo envolver for k,v in ele.items()en try...except (AttributeError, TypeError). Si se eleva la excepción, usted sabe eleno tiene itemsque produce un iterable ...
cowbert
@Padraic Cunningham Su caso hipotético de borde requeriría que su clase personalizada "100% no es un dict" tuviera un items()método 2. que resultó ser un iterable y 3. donde cada elemento de ese iterable se puede representar como un 2 -tupla. En este punto, su objeto personalizado ya ha implementado la mitad de un dict. A los efectos del código que figura, solo nos preocupamos por la expansión condicional del elemento anidado, y su objeto personalizado ya lo implementa. (Es un poco irónico que critique el comentario de @Alexander Ryzhov por ser demasiado general, pero ahora plantea un caso general)
cowbert
1
@PadraicCunningham Prefiero no enseñar a las personas que no están demasiado familiarizadas con los antipatrones de Python, para empezar. Hay muy pocos casos de uso en Python que requieran un control de tipo explícito; la mayoría proviene de heredar una mala implementación para empezar ('objeto de dios, anulación de construcciones estándar de biblioteca / lenguaje, etc.) La pregunta original es en sí misma un problema XY. ¿Por qué el OP necesita verificar el tipo? Porque de acuerdo con su código, lo que realmente quieren hacer es verificar si un elemento de su colección se comporta como una colección (implementos items()que producen un iterativo anidado).
cowbert
44
@AlexanderRyzhov ¿Por qué no publicar ese enfoque como respuesta? Por cierto, como Josh Kelley ha comentado anteriormente sugiriendo collections.abc.Mapping, una nota podría ser apropiada de que son iguales, pero collections.abcno está disponible antes de Python 3.3. collections.Mappingalias todavía está disponible en Python 3.6, pero no está documentado, por lo que probablemente uno debería preferir collections.abc.Maping.
Yushin Washio
5

¿Cómo comprobarías si una variable es un diccionario en Python?

Esta es una excelente pregunta, pero es lamentable que la mayoría de los clientes potenciales de respuesta upvoted con una mala recomendación, type(obj) is dict.

(Tenga en cuenta que tampoco debe usarlo dictcomo un nombre de variable: es el nombre del objeto integrado).

Si está escribiendo código que será importado y utilizado por otros, no presuma que usarán el dict incorporado directamente, lo que hace que su presunción haga que su código sea más inflexible y, en este caso, cree errores fácilmente ocultos que no confundirían el programa .

Sugiero encarecidamente, a los fines de la corrección, la facilidad de mantenimiento y la flexibilidad para los futuros usuarios, que nunca tengan expresiones unidiomáticas menos flexibles en su código cuando haya expresiones idiomáticas más flexibles.

ises una prueba de identidad de objeto . No admite la herencia, no admite ninguna abstracción y no admite la interfaz.

Así que proporcionaré varias opciones que sí.

Herencia de apoyo:

Esta es la primera recomendación que haría, ya que permite a los usuarios para abastecer a sus propias subclase de dict, o una OrderedDict, defaultdicto Counterdesde el módulo de las colecciones:

if isinstance(any_object, dict):

Pero hay opciones aún más flexibles.

Abstracciones de apoyo:

from collections.abc import Mapping

if isinstance(any_object, Mapping):

Esto permite al usuario de su código usar su propia implementación personalizada de una asignación abstracta, que también incluye cualquier subclase de dict, y aún así obtener el comportamiento correcto.

Usa la interfaz

Comúnmente escuchas el consejo de OOP, "programa a una interfaz".

Esta estrategia aprovecha el polimorfismo de Python o la tipificación de patos.

Entonces, solo intente acceder a la interfaz, capturando los errores esperados específicos ( AttributeErroren caso de que no haya .itemsy TypeErroren caso de itemsque no se pueda llamar ) con un respaldo razonable, y ahora cualquier clase que implemente esa interfaz le dará sus elementos (la nota .iteritems()se ha ido en Python 3):

try:
    items = any_object.items()
except (AttributeError, TypeError):
    non_items_behavior(any_object)
else: # no exception raised
    for item in items: ...

Quizás piense que el uso de tipear pato de esta manera va demasiado lejos al permitir demasiados falsos positivos, y puede ser, dependiendo de sus objetivos para este código.

Conclusión

No lo use ispara verificar los tipos de flujo de control estándar. Use isinstance, considere abstracciones como Mappingo MutableMapping, y considere evitar la verificación de tipos por completo, usando la interfaz directamente.

Aaron Hall
fuente
3

El OP no excluyó la variable inicial, por lo que para completar aquí es cómo manejar el caso genérico de procesar un supuesto diccionario que puede incluir elementos como diccionarios.

También siguiendo la forma recomendada pura de Python (3.8) para probar el diccionario en los comentarios anteriores.

from collections.abc import Mapping

dict = {'abc': 'abc', 'def': {'ghi': 'ghi', 'jkl': 'jkl'}}

def parse_dict(in_dict): 
    if isinstance(in_dict, Mapping):
        for k, v in in_dict.items():
            if isinstance(v, Mapping):
                for k, v in v.items():
                    print(k, v)
            else:
                print(k, v)

parse_dict(dict)
CodeMantle
fuente
dict.iteritems()no existe en Python 3, deberías usarlo dict.items()en su lugar.
Arkelis
@Arkelis Vaya, acabo de copiar / pegar esa parte, gracias por señalarlo, corregido ahora.
CodeMantle