Как проверить, является ли переменная словарём в Python?

214

Как бы вы проверили, является ли переменная словарём в python?

Например, я бы хотел, чтобы он просматривал значения в словаре, пока не найдет словарь. Затем переберите тот, который он находит:

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)
Райли
источник
Также stackoverflow.com/questions/378927/… (который отмечен как дубликат вышеупомянутого).
NPE
40
Нет, это не тот же вопрос. Ответ на этот и другие вопросы, перечисленные здесь, содержат практически одинаковую информацию. Но ответ на вопрос «Как проверить, является ли переменная словарём в python», является «Использовать type () или isinstance ()», что приводит к новому вопросу, в чем разница между type () и isinstance () , Но человек, задающий первый вопрос, не может знать об этом, пока на первый вопрос не будет дан ответ. Ergo, разные вопросы, которые имеют значение, когда вы ищете свой вопрос на сайте.
13
Я согласен с @mbakeranalecta Я пришел сюда в поисках ответа на вопрос "Как проверить, является ли переменная словарём в Python?" и я бы никогда не подумал посмотреть мой ответ на «Различия между isinstance () и type () в python».
Лодебари
5
Для проверки, является ли переменная словарным, в частности, вам, вероятно, следует использовать isinstance(v, collections.abc.Mapping). Другими словами, это не является точной копией «Различия между isinstance () и type ()».
Джош Келли

Ответы:

279

Вы могли бы использовать if type(ele) is dict или использовать то, isinstance(ele, dict)что будет работать, если вы подкласс 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)
Падрайк Каннингем
источник
70
Я вниз проголосовал этот ответ , потому что правильный ответ на общий вопрос: isinstance(ele, collections.Mapping). Она работает dict(), collections.OrderedDict()и collections.UserDict(). Пример в вопросе достаточно специфичен для ответа Падриака, но не достаточно хорош для общего случая.
Александр Рыжов
4
Я отклонил этот ответ, потому что, если вы собираетесь вызвать, ele.items()почему вы проверяете тип? ЭСПЦ / уток-типирование работает здесь, просто обернуть for k,v in ele.items()в try...except (AttributeError, TypeError). Если возникает исключение, вы знаете, eleчто нет, itemsчто приводит к итерации ...
Cowbert
@Padraic Cunningham Ваш гипотетический крайний случай потребовал бы, чтобы у вашего пользовательского класса "100% not a dict" был items()метод 2., который, как оказалось, давал итерируемое и 3. где каждый элемент этого итерируемого можно представить как 2 -кратный. На данный момент ваш пользовательский объект уже реализовал половину диктата. В целях перечисленного кода мы заботились только об условном расширении вложенного элемента, и ваш пользовательский объект уже реализует это. (Немного иронично, что вы критикуете комментарий @Alexander Ryzhov за то, что он слишком общий, но теперь поднимите общий случай)
cowbert
1
@PadraicCunningham Я бы предпочел не учить людей, которые не слишком знакомы с анти-паттернами Python для начала. В Python очень мало прецедентов, требующих явной проверки типов - большинство из них проистекает из наследования плохой реализации для начала («объект бога», переопределение стандартных библиотечных / языковых конструкций и т. Д.). Исходный вопрос сам по себе является проблемой XY. Почему ОП должен проверять тип? Потому что в соответствии с их кодом они действительно хотят проверить, ведет ли элемент в своей коллекции как коллекцию (реализует, items()что дает вложенную итерацию).
Cowbert
4
@AlexanderRyzhov Почему бы не опубликовать этот подход в качестве ответа? Кстати, как прокомментировал Джош Келли , предлагая collections.abc.Mappingзаметить, что они могут быть одинаковыми, но collections.abcнедоступны до Python 3.3. collections.Mappingпсевдоним все еще доступен в Python 3.6, но не документирован, так что, вероятно, следует отдать предпочтение collections.abc.Maping.
Юшин
5

Как бы вы проверили, является ли переменная словарём в Python?

Это отличный вопрос, но, к сожалению, самый голосующий голос приводит к плохой рекомендации type(obj) is dict.

(Обратите внимание, что вы также не должны использовать dictв качестве имени переменной - это имя встроенного объекта.)

Если вы пишете код, который будет импортироваться и использоваться другими, не предполагайте, что они будут использовать встроенную функцию dict напрямую - делая это предположение, делает ваш код более негибким, и в этом случае создавайте легко скрытые ошибки, которые не будут вызывать ошибку программы. ,

Я настоятельно рекомендую, для целей корректности, удобства обслуживания и гибкости для будущих пользователей, никогда не иметь менее гибких, недиоматических выражений в вашем коде, когда есть более гибкие идиоматические выражения.

isявляется испытанием для идентичности объекта . Он не поддерживает наследование, не поддерживает абстракции и не поддерживает интерфейс.

Поэтому я предоставлю несколько вариантов, которые делают.

Поддержка наследования:

Это первая рекомендация я хотел бы сделать, потому что она позволяет пользователям поставить свой собственный подкласс Dict, или OrderedDict, defaultdictили Counterиз коллекции модуля:

if isinstance(any_object, dict):

Но есть еще более гибкие варианты.

Поддерживающие абстракции:

from collections.abc import Mapping

if isinstance(any_object, Mapping):

Это позволяет пользователю вашего кода использовать собственную пользовательскую реализацию абстрактного отображения, которая также включает в себя любой подкласс класса dict, и при этом получать правильное поведение.

Используйте интерфейс

Вы обычно слышите совет ООП "программа для интерфейса".

Эта стратегия использует преимущества полиморфизма Python или типизации уток.

Поэтому просто попытайтесь получить доступ к интерфейсу, перехватывая определенные ожидаемые ошибки ( AttributeErrorв случае, если их нет, .itemsа TypeErrorв случае, если они itemsне могут быть вызваны) с разумным отступлением - и теперь любой класс, реализующий этот интерфейс, выдаст вам свои элементы (примечание .iteritems()отсутствует в Python 3):

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

Возможно, вы подумали, что использование утки, как это, заходит слишком далеко, допуская слишком много ложных срабатываний, и это может быть в зависимости от ваших целей для этого кода.

Вывод

Не используйте isдля проверки типов для стандартного потока управления. Используйте isinstance, рассматривайте абстракции как Mappingили MutableMapping, и вообще избегайте проверки типов, используя интерфейс напрямую.

Аарон Холл
источник
3

OP не исключил начальную переменную, поэтому для полноты здесь показано, как обрабатывать общий случай обработки предполагаемого словаря, который может включать элементы в качестве словарей.

Также, следуя чистому Python (3.8), рекомендуем проверить способ словаря в приведенных выше комментариях.

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
источник
dict.iteritems()не существует в Python 3, вы должны использовать dict.items()вместо этого.
Аркелис
@Arkelis Упс, только что скопировал / вставил эту часть, спасибо, что указал на это - исправлено сейчас.
CodeMantle