Могу ли я использовать выражение генератора для печати пар ключ-значение из списка словарей?

У меня есть список словарей:

dictlist = [{'key1':'value1', 'key2':'value2'}, {'key3':'value3', 'key4':'value4'}]

Я в настоящее время печатаю ключ, пары значений, как это:

for d in dictlist:
  for key in d:
    print .format('{} ---> {}').format(key,d[key])

Как бы я использовал выражение генератора, чтобы уменьшить количество строк кода?

Редактировать: я пытаюсь ознакомиться с выражениями генератора, а не просто сократить строки кода!

5 ответов

Взгляните на документы по выражениям на языке python. Это все вещи, которые просто представляют ценности того или иного рода, различной сложности. Они не являются побочными эффектами (такими как ввод / вывод) или преобразованиями в изменяемые данные.Выражения генератора, как следует из названия, являются просто другим видом выражения.

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

В частности, выражения генератора кажутся мне тесно связанными с функциональной парадигмой программирования на Python. Они дают значения, как и следовало ожидать от математически определенного ряда, как the generator yielding all the Fibonacci numbersдля примера. Учитывая эту функциональную парадигму,использование генераторов специально для создания побочных эффектов, вероятно, являетсязапахом кода.

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

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

def my_printer(dictlist):
    for d int dictlist: # renamed to not shadow dict
        for key in dict:
            print .format('{} ---> {}').format(key,dict[key])
            yield

Тогда вы можете напечатать такие элементы, как:

>>> p = my_printer(dictlist)
>>> p.next()
'some stuff 1'
>>> p.next()
'some stuff 2'

И т.п.

Это не совсем то, что вы просили, хотя.

Чтобы ответить на ваш вопрос более прямо, если вы оставите print Заявление из этого, вы могли бы получить что-то, что выглядит как уплощение вашего вложенного for петли:

>>> (('{} ---> {}').format(k,v) for d in dictlist for k,v in d.items())

Или на самом деле, так как (k,v) это просто кортеж, который вы передаете в том же порядке, вы можете использовать оператор сплат * распаковать его напрямую как параметры в ваш format вызов:

>>> (('{} ---> {}').format(*p) for d in dictlist for p in d.items())

Наконец, вы можете удалить промежуточный список d.items() используя iteritems функция вместо:

>>> (('{} ---> {}').format(*p) for d in dictlist for p in d.iteritems())

затем выведите результат с помощью обычного цикла for:

>>> for x in _:
    print x


key2 ---> value2
key1 ---> value1
key3 ---> value3
key4 ---> value4

Не было бы реальной выгоды сделать ваш код горизонтальным, а не вертикальным, но вы можете сделать это следующим образом:

from __future__ import print_function
from itertools import imap
print(*(' ---> '.join(imap(str, item)) for dic in dictlist for item in dic.iteritems()), sep='\n')

Результат:

key2 ---> value2
key1 ---> value1
key3 ---> value3
key4 ---> value4

Обратите внимание, что map() для преобразования нестроковых элементов в строки, чтобы их можно было соединить с join(), Если вы уверены, что ключи и значения всегда будут строками, вы можете просто использовать ' ---> '.join(item) вместо.

Хм, что у вас, наверное, самая читаемая версия. Я бы, наверное, пошел с этим.

Но если вы хотите более компактную версию, как насчет:

>>> dictlist = [{'key1':'value1', 'key2':'value2'}, {'key3':'value3', 'key4':'value4'}]
>>> list((k,v) for d in dictlist for k,v in d.iteritems())
[('key2', 'value2'), ('key1', 'value1'), ('key3', 'value3'), ('key4', 'value4')]

Примечание: внешнее list это просто заполнитель. Вы должны позвонить поставить вызов функции на его место.

Если вы действительно хотите, вы можете перебирать выражение генератора, которое печатает сам материал, и просто отбрасывать то, что вы перебираете:

for _ in (print('{} ---> {}'.format(key,dict[key])) for dict in dictlist for key in dict):
    pass

Я действительно не вижу причин делать это, хотя, это не очень читабельно. Предполагается, что Python 3 или from __future__ import print_functionтак что вы можете использовать print в выражении.

Вы можете использовать выражение генератора и itertools в одну строку это.

from itertools import chain, imap

print '\n'.join('{} ---> {}'.format(k,v) for k, v in chain.from_iterable(d.iteritems() for d in dictlist))

d.itervalues() возвращает генератор всех значений в каждом словаре. itertools.chain берет все эти генераторы и объединяет их в один генератор. itertools.imap Функция конвертирует все ваши значения в строки, чтобы их можно было распечатать. Эти значения затем подаются на .join метод, который помещает новую строку между каждым значением и печатает его.

Другие вопросы по тегам