Como verificar se uma variável é um dicionário em Python?

214

Como você verificaria se uma variável é um dicionário em python?

Por exemplo, eu gostaria que ele repetisse os valores no dicionário até encontrar um dicionário. Em seguida, faça um loop pelo que encontrar:

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
fonte
Também stackoverflow.com/questions/378927/… (que está marcado como uma duplicata da acima).
NPE
40
Não, não é a mesma pergunta. A resposta a esta pergunta e às outras perguntas listadas aqui contêm substancialmente as mesmas informações. Mas a resposta para "Como verificar se uma variável é um dicionário em python" é "Use type () ou isinstance ()", o que leva a uma nova pergunta, qual é a diferença entre type () e isinstance () . Mas a pessoa que faz a primeira pergunta não pode saber disso até que a primeira seja respondida. Portanto, perguntas diferentes, o que importa quando você está procurando sua pergunta no site.
13
Concordo com @mbakeranalecta Vim aqui procurando a resposta para a pergunta "Como verificar se uma variável é um dicionário em Python?" e nunca pensei em procurar minha resposta em "Diferenças entre isinstance () e type () em python".
Lodebari
5
Para verificar se uma variável é um dicionário em particular, você provavelmente deve usar isinstance(v, collections.abc.Mapping). Em outras palavras, essa não é uma duplicata exata de "Diferenças entre isinstance () e type ()".
Josh Kelley

Respostas:

279

Você poderia usar if type(ele) is dictou usar o isinstance(ele, dict)que funcionaria se você tivesse subclassificado 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
fonte
70
Eu down-votado esta resposta, porque a resposta certa para a pergunta geral é: isinstance(ele, collections.Mapping). Ele trabalha para dict(), collections.OrderedDict()e collections.UserDict(). O exemplo da pergunta é específico o suficiente para a resposta de Padriac funcionar, mas não é bom o suficiente para o caso geral.
Alexander Ryzhov
3
A resposta foi negativa porque, se você vai invocar, ele.items()por que está verificando o tipo? O EAFP / digitação de pato funciona aqui, apenas envolva for k,v in ele.items()-o try...except (AttributeError, TypeError). Se exceção é gerada, você sabe elenão tem itemsque produz um iterable ...
cowbert
@Padraic Cunningham Seu caso hipotético de ponta exigiria que sua classe personalizada "100% não seja um ditado" 1. tivesse um items()método 2. que por acaso produzia um iterável e 3. onde cada elemento desse iterável pode ser representado como um 2 -tuplo. Neste ponto, seu objeto personalizado já implementou metade de um ditado. Para os fins do código listado, apenas nos preocupamos com a expansão condicional do elemento aninhado, e seu objeto personalizado já o implementa. (É um pouco irônico que você critica o comentário de @Alexander Ryzhov por ser muito geral, mas agora levantar um caso geral)
cowbert
1
@PadraicCunningham Prefiro não ensinar anti-padrões para pessoas que não são excessivamente familiarizadas com Python. Existem muito poucos casos de uso no Python que requerem verificação de tipo explícita - a maioria resulta da herança de uma implementação ruim ('objeto de deus, substituindo construções padrão de biblioteca / linguagem, etc.) A pergunta original é um problema XY. Por que o OP precisa verificar o tipo? Porque, de acordo com o código, o que eles realmente querem fazer é verificar se um item em sua coleção se comporta como uma coleção (implementos items()que produzem uma iterável aninhada).
cowbert
4
@AlexanderRyzhov Por que não postar essa abordagem como resposta? BTW como Josh Kelley comentou acima sugerindo collections.abc.Mapping, uma nota pode ser apropriado que eles são o mesmo, mas collections.abcnão está disponível antes de Python 3.3. collections.MappingO alias ainda está disponível no Python 3.6, mas não está documentado, portanto, provavelmente, deve-se preferir collections.abc.Maping.
Yushin Washio
5

Como você verificaria se uma variável é um dicionário em Python?

Essa é uma excelente pergunta, mas é lamentável que a resposta mais votada leve a uma recomendação ruim type(obj) is dict.

(Observe que você também não deve usar dictcomo um nome de variável - é o nome do objeto interno.)

Se você estiver escrevendo um código que será importado e usado por outras pessoas, não presuma que eles usarão o dict diretamente - tornando essa suposição mais flexível e, nesse caso, crie bugs facilmente ocultos que não prejudicariam o programa. .

Eu sugiro fortemente, para fins de correção, manutenção e flexibilidade para usuários futuros, nunca ter expressões unidiomatic menos flexíveis em seu código quando houver expressões idiomáticas mais flexíveis.

isé um teste para a identidade do objeto . Não suporta herança, não suporta abstração e não suporta a interface.

Então, vou fornecer várias opções que o fazem.

Herança de suporte:

Esta é a primeira recomendação gostaria de fazer, porque permite que os usuários forneçam seus próprios subclasse de dict, ou uma OrderedDict, defaultdictou Counterdas coleções módulo:

if isinstance(any_object, dict):

Mas existem opções ainda mais flexíveis.

Abstrações de suporte:

from collections.abc import Mapping

if isinstance(any_object, Mapping):

Isso permite que o usuário do seu código use sua própria implementação personalizada de um mapeamento abstrato, que também inclui qualquer subclasse de dict, e ainda obtenha o comportamento correto.

Use a interface

Você geralmente ouve os conselhos do OOP, "programa para uma interface".

Essa estratégia aproveita o polimorfismo do Python ou a digitação de patos.

Então, basta tentar acessar a interface, pegando os erros específicos esperado ( AttributeErrorno caso, não há .itemse TypeErrorno caso itemsnão é resgatável) com um fallback razoável - e agora qualquer classe que implementa essa interface vai lhe dar os seus itens (nota .iteritems()é ido em Python 3):

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

Talvez você pense que usar a digitação de pato dessa maneira vá longe demais ao permitir muitos falsos positivos, e pode ser, dependendo dos seus objetivos para este código.

Conclusão

Não use ispara verificar os tipos quanto ao fluxo de controle padrão. Use isinstance, considere abstrações como Mappingou MutableMappinge evite a verificação de tipo completamente, usando a interface diretamente.

Aaron Hall
fonte
3

O OP não excluiu a variável inicial, portanto, para completar, veja como lidar com o caso genérico de processar um suposto dicionário que pode incluir itens como dicionários.

Também seguindo a maneira recomendada pura de Python (3.8) para testar o dicionário nos comentários acima.

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
fonte
dict.iteritems()não existe no Python 3, você deve usar dict.items().
Arkelis
@Arkelis Opa, apenas copiei / colei essa parte, obrigado por apontá-la - corrigida agora.
CodeMantle