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

#dictionary #key #python

#словарь #Клавиша #python

Вопрос:

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

 a = {
  'shelf':{
    'book':{'title':'the catcher in the rye', 'author':'j d salinger'}
  }
}  

b = {
  'shelf':[
    {'book':{'title':'kafka on the shore', 'author':'haruki murakami'}},
    {'book':{'title':'atomised', 'author':'michel houellebecq'}}
  ]    
}
  

Вот мой метод чтения названий каждой книги на полке.

 def print_books(d):
  if(len(d['shelf']) == 1):
    print d['shelf']['book']['title']
  else:
    for book in d['shelf']:
      print book['book']['title']
  

Это работает, но не выглядит аккуратно или по-питоновски. Цикл for завершается с ошибкой при единственном значении, отсюда if / else.

Можете ли вы улучшить это?

Комментарии:

1. Почему, о, почему ваши структуры данных не согласованы…

2. Отбросить средний dict, который в любом случае имеет только один ключ? И «полка» не была бы особенно актуальна только для одного элемента. Дзен Python: «Особые случаи недостаточно особенные, чтобы нарушать правила».

3. @Iganacio хаха, это не я. Данные поступают следующим образом из сервиса Yahoo.

Ответ №1:

Учитывая, что ваш код сломается, если у вас есть список с одним элементом (и я думаю, что так и должно быть), если вы действительно не можете изменить свою структуру данных, это немного более надежно и логично:

 def print_books(d):
    if isinstance(d['shelf'], dict):
        print d['shelf']['book']['title']
    else:
        for book in d['shelf']:
            print book['book']['title']
  

Ответ №2:

Почему не всегда сопоставлять ‘shelf’ со списком элементов, но в случае с одним элементом это … список с одним элементом? Тогда вы всегда сможете одинаково обращаться с каждой книжной полкой.

Ответ №3:

 def print_books(d):
    container = d['shelf']
    books = container if isinstance(container, list) else [container['book']]
    books = [ e['book'] for e in books ] 
    for book in books:
        print book['title']
  

Ответ №4:

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

 def print_books(d):
    books = d['shelf'] if type(d['shelf']) is list else [ d['shelf'] ]
    for book in books:
        print book['book']['title']
  

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