Ответ 1
Используя обычный рецепт grouper recipe, вы можете сделать:
Python 2:
d = dict(itertools.izip_longest(*[iter(l)] * 2, fillvalue=""))
Python 3:
d = dict(itertools.zip_longest(*[iter(l)] * 2, fillvalue=""))
l = ["a", "b", "c", "d", "e"]
Я хочу преобразовать этот список в словарь, например:
d = {"a": "b", "c": "d", "e": ""}
Итак, в основном, evens будут ключами, тогда как коэффициенты будут значениями. Я знаю, что могу сделать это с помощью "непифонного" способа, такого как цикл for с операторами if, но я считаю, что для этого должен быть более "питонический" способ. Итак, я ценю любую помощь:)
Используя обычный рецепт grouper recipe, вы можете сделать:
Python 2:
d = dict(itertools.izip_longest(*[iter(l)] * 2, fillvalue=""))
Python 3:
d = dict(itertools.zip_longest(*[iter(l)] * 2, fillvalue=""))
Если вы все еще думаете, что! Вы не были бы одиноки, на самом деле это не так сложно, позвольте мне объяснить.
Мы хотим превратить следующий список в словарь с использованием нечетных записей (считая от 1) в качестве ключей, сопоставленных с их последовательными четными записями.
l = ["a", "b", "c", "d", "e"]
Чтобы создать словарь, мы можем использовать встроенную функцию dict
для Типы сопоставления в соответствии с руководством, поддерживая следующие методы.
dict(one=1, two=2)
dict({'one': 1, 'two': 2})
dict(zip(('one', 'two'), (1, 2)))
dict([['two', 2], ['one', 1]])
Последний вариант предполагает, что мы поставляем список списков с 2 значениями или (key, value)
кортежей, поэтому мы хотим превратить наш последовательный список в:
l = [["a", "b"], ["c", "d"], ["e",]]
Мы также вводим функцию zip
, одну из встроенных функций, которая в руководстве объясняется:
возвращает список кортежей, где i-й кортеж содержит i-й элемент из каждого из аргументов
Другими словами, если мы можем превратить наш список в два списка a, c, e
и b, d
, то zip
сделает все остальное.
Slicings, который мы видим с Strings, а также далее в Список разделов, который в основном использует диапазон или короткий фрагмент, но это то, что длинный фрагмент выглядит так, и мы можем выполнить шаг:
>>> l[::2]
['a', 'c', 'e']
>>> l[1::2]
['b', 'd']
>>> zip(['a', 'c', 'e'], ['b', 'd'])
[('a', 'b'), ('c', 'd')]
>>> dict(zip(l[::2], l[1::2]))
{'a': 'b', 'c': 'd'}
Несмотря на то, что это самый простой способ понять задействованную механику, есть недостаток, потому что срезы - это новые объекты списка каждый раз, как видно из этого примера клонирования:
>>> a = [1, 2, 3]
>>> b = a
>>> b
[1, 2, 3]
>>> b is a
True
>>> b = a[:]
>>> b
[1, 2, 3]
>>> b is a
False
Несмотря на то, что b выглядит как два отдельных объекта, и поэтому мы предпочитаем использовать рецепт группы > .
Хотя групповик объясняется как часть модуля itertools, он отлично работает с основными функциями.
Какой-то серьезный вуду? =) Но на самом деле это не что иное, как немного синтаксического сахара для специй, рецепт группокера выполняется следующим выражением.
*[iter(l)]*2
Что более или менее соответствует двум аргументам одного и того же итератора, заключенного в список, если это имеет смысл. Давайте разложим его, чтобы помочь пролить некоторый свет.
>>> l*2
['a', 'b', 'c', 'd', 'e', 'a', 'b', 'c', 'd', 'e']
>>> [l]*2
[['a', 'b', 'c', 'd', 'e'], ['a', 'b', 'c', 'd', 'e']]
>>> [iter(l)]*2
[<listiterator object at 0x100486450>, <listiterator object at 0x100486450>]
>>> zip([iter(l)]*2)
[(<listiterator object at 0x1004865d0>,),(<listiterator object at 0x1004865d0>,)]
>>> zip(*[iter(l)]*2)
[('a', 'b'), ('c', 'd')]
>>> dict(zip(*[iter(l)]*2))
{'a': 'b', 'c': 'd'}
Как вы можете видеть, адреса для двух итераторов остаются теми же, поэтому мы работаем с тем же итератором, который затем zip сначала получает ключ, а затем значение и ключ и значение каждый раз, когда наступает тот же самый итератор, чтобы выполнить что мы сделали с кусочками гораздо продуктивнее.
Вы выполнили бы очень то же самое со следующим, которое может содержать меньший Что такое?.
>>> it = iter(l)
>>> dict(zip(it, it))
{'a': 'b', 'c': 'd'}
Как насчет пустого ключа e
, если вы заметили, что он отсутствует во всех примерах, потому что zip
выбирает самый короткий из двух аргументов, так что нам делать.
Ну, одно решение может добавлять пустые значения в списки нечетной длины, вы можете использовать append
и инструкцию if
, которая могла бы сделать трюк, хотя и немного скучный, правильно?
>>> if len(l) % 2:
... l.append("")
>>> l
['a', 'b', 'c', 'd', 'e', '']
>>> dict(zip(*[iter(l)]*2))
{'a': 'b', 'c': 'd', 'e': ''}
Теперь, прежде чем вы отмахнетесь, чтобы пойти типа from itertools import izip_longest
, вы можете быть удивлены, узнав, что это не требуется, мы можем добиться того же, даже лучшего ИМХО, только с встроенными функциями.
Я предпочитаю использовать функцию map() вместо izip_longest(), которая не только использует короткие синтаксис не требует импорта, но при необходимости автоматически может присваивать фактическое None
пустое значение.
>>> l = ["a", "b", "c", "d", "e"]
>>> l
['a', 'b', 'c', 'd', 'e']
>>> dict(map(None, *[iter(l)]*2))
{'a': 'b', 'c': 'd', 'e': None}
Сравнивая эффективность двух методов, как указано в KursedMetal, ясно, что модуль itertools намного превосходит функцию отображения на больших томах, как показывает эталон против 10 миллионов записей.
$ time python -c 'dict(map(None, *[iter(range(10000000))]*2))'
real 0m3.755s
user 0m2.815s
sys 0m0.869s
$ time python -c 'from itertools import izip_longest; dict(izip_longest(*[iter(range(10000000))]*2, fillvalue=None))'
real 0m2.102s
user 0m1.451s
sys 0m0.539s
Однако стоимость импорта модуля сказывается на меньших наборах данных, при этом карта возвращается гораздо быстрее, примерно до 100 тысяч записей, когда они начинают прибывать с головы до головы.
$ time python -c 'dict(map(None, *[iter(range(100))]*2))'
real 0m0.046s
user 0m0.029s
sys 0m0.015s
$ time python -c 'from itertools import izip_longest; dict(izip_longest(*[iter(range(100))]*2, fillvalue=None))'
real 0m0.067s
user 0m0.042s
sys 0m0.021s
$ time python -c 'dict(map(None, *[iter(range(100000))]*2))'
real 0m0.074s
user 0m0.050s
sys 0m0.022s
$ time python -c 'from itertools import izip_longest; dict(izip_longest(*[iter(range(100000))]*2, fillvalue=None))'
real 0m0.075s
user 0m0.047s
sys 0m0.024s
Ничего не трогай! =)
NJoy!
Я бы пошел на рекурсии:
l = ['a', 'b', 'c', 'd', 'e', ' ']
d = dict([(k, v) for k,v in zip (l[::2], l[1::2])])
Не уверен, поможет ли это вам или нет, но он работает со мной:
l = ["a", "b", "c", "d", "e"]
outRes = dict((l[i], l[i+1]) if i+1 < len(l) else (l[i], '') for i in xrange(len(l)))