Перебор всех двух элементов в списке

209

Как сделать forцикл или понимание списка, чтобы каждая итерация давала мне два элемента?

l = [1,2,3,4,5,6]

for i,k in ???:
    print str(i), '+', str(k), '=', str(i+k)

Вывод:

1+2=3
3+4=7
5+6=11
jackhab
источник
2
Для перекрытия пары: stackoverflow.com/questions/5434891/...
user202729

Ответы:

231

Вам нужна pairwise()(или grouped()) реализация.

Для Python 2:

from itertools import izip

def pairwise(iterable):
    "s -> (s0, s1), (s2, s3), (s4, s5), ..."
    a = iter(iterable)
    return izip(a, a)

for x, y in pairwise(l):
   print "%d + %d = %d" % (x, y, x + y)

Или, в более общем плане:

from itertools import izip

def grouped(iterable, n):
    "s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), (s2n,s2n+1,s2n+2,...s3n-1), ..."
    return izip(*[iter(iterable)]*n)

for x, y in grouped(l, 2):
   print "%d + %d = %d" % (x, y, x + y)

В Python 3 вы можете заменить izipвстроенной zip()функцией и удалить import.

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

NB : Это не следует путать с pairwiseрецептом в собственной itertoolsдокументации Python , который дает s -> (s0, s1), (s1, s2), (s2, s3), ..., как указал @lazyr в комментариях.

Небольшое дополнение для тех, кто хотел бы выполнить проверку типов с помощью Mypy на Python 3:

from typing import Iterable, Tuple, TypeVar

T = TypeVar("T")

def grouped(iterable: Iterable[T], n=2) -> Iterable[Tuple[T, ...]]:
    """s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), ..."""
    return zip(*[iter(iterable)] * n)
Johnsyweb
источник
16
Не следует путать с парной функцией, предложенной в разделе рецептов itertools , которая приводит к получениюs -> (s0,s1), (s1,s2), (s2, s3), ...
Lauritz V. Thaulow
1
Это делает другое. Ваша версия дает только половину количества пар по сравнению с itertoolsфункцией рецепта с тем же именем. Конечно, ваш быстрее ...
Свен Марнач
А? Ваша функция и функция, о которой я говорил, делали разные вещи, и это было точкой моего комментария.
Лауриц В. Таулов
5
БЫТЬ ОСТОРОЖЕН! Используя эти функции, вы рискуете не выполнять итерации по последним элементам итерации. Пример: list (grouped ([1,2,3], 2)) >>> [(1, 2)] .. когда вы ожидаете [(1,2), (3,)]
egafni
4
@ Erik49: В случае, указанном в вопросе, не имеет смысла иметь «неполный» кортеж. Если вы хотите включить неполный кортеж, вы можете использовать izip_longest()вместо izip(). Например: list(izip_longest(*[iter([1, 2, 3])]*2, fillvalue=0))-> [(1, 2), (3, 0)]. Надеюсь это поможет.
Johnsyweb
191

Ну, вам нужен кортеж из 2 элементов, так

data = [1,2,3,4,5,6]
for i,k in zip(data[0::2], data[1::2]):
    print str(i), '+', str(k), '=', str(i+k)

Куда:

  • data[0::2] означает создать подмножество элементов, которые (index % 2 == 0)
  • zip(x,y) создает коллекцию кортежей из коллекций x и y с одинаковыми индексными элементами.
Маргус
источник
8
Это также может быть расширено, если требуется более двух элементов. Для напримерfor i, j, k in zip(data[0::3], data[1::3], data[2::3]):
lifebalance
19
Намного чище, чем использовать импорт и определять функции!
kmarsh
7
@kmarsh: Но это работает только с последовательностями, функция работает с любой итерацией; и это использует O (N) дополнительное пространство, функция не делает; с другой стороны, это обычно быстрее. Есть веские причины для выбора одного или другого; бояться importне один из них.
abarnert
77
>>> l = [1,2,3,4,5,6]

>>> zip(l,l[1:])
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]

>>> zip(l,l[1:])[::2]
[(1, 2), (3, 4), (5, 6)]

>>> [a+b for a,b in zip(l,l[1:])[::2]]
[3, 7, 11]

>>> ["%d + %d = %d" % (a,b,a+b) for a,b in zip(l,l[1:])[::2]]
['1 + 2 = 3', '3 + 4 = 7', '5 + 6 = 11']
pyanon
источник
1
Это не работает на Python-3.6.0, но все еще работает на Python-2.7.10
Хамид Рохани
6
@HamidRohani zipвозвращает zipобъект в Python 3, который не является подписным. Сначала его нужно преобразовать в последовательность ( list, tupleи т. Д.), Но «не работает» немного растянуто.
vaultah
58

Простое решение

l = [1, 2, 3, 4, 5, 6]

для i в диапазоне (0, len (l), 2):
    print str (l [i]), '+', str (l [i + 1]), '=', str (l [i] + l [i + 1])
taskinoor
источник
1
Что делать, если ваш список не является четным, и вы хотите просто показать последний номер, как он есть?
Ганс де Йонг
@HansdeJong не понял тебя. Пожалуйста, объясните немного больше.
Taskinoor
2
Спасибо. Я уже понял, как это сделать. Проблема заключалась в том, что если бы у вас был список, в котором не было даже количества чисел, он мог бы получить ошибку индекса. Решил это с попыткой: кроме:
Ханс де Йонг
Или ((l[i], l[i+1])for i in range(0, len(l), 2))для генератора, может быть легко модифицирован для более длинных кортежей.
Базель Шишани
44

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

def pairwise(it):
    it = iter(it)
    while True:
        try:
            yield next(it), next(it)
        except StopIteration:
            # no more elements in the iterator
            return

Эта it = iter(it)часть гарантирует, что itэто итератор, а не итерация. Если itуже есть итератор, эта строка не используется.

Использование:

for a, b in pairwise([0, 1, 2, 3, 4, 5]):
    print(a + b)
мышей
источник
2
Такое решение позволяет обобщить на размер кортежей> 2
guilloptero
1
Это решение также работает, если itявляется только итератором, а не итерацией. Другие решения, похоже, полагаются на возможность создания двух независимых итераторов для последовательности.
скайкинг
Я нашел этот подход на stackoverflow.com/a/16815056/2480481, прежде чем увидеть этот ответ. Это чище, проще, чем иметь дело с zip ().
m3nda
2
Мне нравится, что это позволяет избежать трехкратного использования памяти в качестве принятого ответа.
Кендзо
Это не очень хорошо работает с forциклами в Python 3.5+ из-за PEP 479 , который заменяет любой вызванный StopIterationв генераторе на RuntimeError.
Сидни
28

Я надеюсь, что это будет еще более элегантный способ сделать это.

a = [1,2,3,4,5,6]
zip(a[::2], a[1::2])

[(1, 2), (3, 4), (5, 6)]
Вивек Сринивасан
источник
18

Если вам интересна производительность, я провел небольшой тест (используя свою библиотеку simple_benchmark) для сравнения производительности решений и включил функцию из одного из моих пакетов:iteration_utilities.grouper

from iteration_utilities import grouper
import matplotlib as mpl
from simple_benchmark import BenchmarkBuilder

bench = BenchmarkBuilder()

@bench.add_function()
def Johnsyweb(l):
    def pairwise(iterable):
        "s -> (s0, s1), (s2, s3), (s4, s5), ..."
        a = iter(iterable)
        return zip(a, a)

    for x, y in pairwise(l):
        pass

@bench.add_function()
def Margus(data):
    for i, k in zip(data[0::2], data[1::2]):
        pass

@bench.add_function()
def pyanon(l):
    list(zip(l,l[1:]))[::2]

@bench.add_function()
def taskinoor(l):
    for i in range(0, len(l), 2):
        l[i], l[i+1]

@bench.add_function()
def mic_e(it):
    def pairwise(it):
        it = iter(it)
        while True:
            try:
                yield next(it), next(it)
            except StopIteration:
                return

    for a, b in pairwise(it):
        pass

@bench.add_function()
def MSeifert(it):
    for item1, item2 in grouper(it, 2):
        pass

bench.use_random_lists_as_arguments(sizes=[2**i for i in range(1, 20)])
benchmark_result = bench.run()
mpl.rcParams['figure.figsize'] = (8, 10)
benchmark_result.plot_both(relative_to=MSeifert)

введите описание изображения здесь

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

Если вы не возражаете дополнительную зависимость то grouperот iteration_utilities, вероятно , будет немного быстрее.

Дополнительные мысли

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

Например, некоторые решения работают только для последовательностей (то есть списков, строк и т. Д.), Например, решения Margus / pyanon / taskinoor, которые используют индексирование, в то время как другие решения работают с любыми итерируемыми (то есть последовательностями и генераторами, итераторами), такими как Johnysweb / mic_e / мои решения.

Затем Johnysweb также предоставил решение, которое работает для других размеров, отличных от 2, в то время как другие ответы - нет (хорошо, iteration_utilities.grouperтакже позволяет установить количество элементов в «группу»).

Тогда возникает также вопрос о том, что должно произойти, если в списке присутствует нечетное количество элементов. Оставшийся предмет должен быть уволен? Должен ли список быть дополнен, чтобы сделать его равным по размеру? Оставшийся предмет должен быть возвращен как один? Другой ответ не касается этого вопроса напрямую, однако, если я ничего не пропустил, все они следуют подходу, согласно которому оставшийся элемент должен быть отклонен (за исключением ответа Taskinoors - который фактически вызовет исключение).

С помощью которого grouperвы можете решить, что вы хотите сделать:

>>> from iteration_utilities import grouper

>>> list(grouper([1, 2, 3], 2))  # as single
[(1, 2), (3,)]

>>> list(grouper([1, 2, 3], 2, truncate=True))  # ignored
[(1, 2)]

>>> list(grouper([1, 2, 3], 2, fillvalue=None))  # padded
[(1, 2), (3, None)]
MSeifert
источник
12

Используйте zipи iterкоманды вместе:

Я считаю это решение iterдовольно элегантным:

it = iter(l)
list(zip(it, it))
# [(1, 2), (3, 4), (5, 6)]

Который я нашел в документации по Python 3 zip .

it = iter(l)
print(*(f'{u} + {v} = {u+v}' for u, v in zip(it, it)), sep='\n')

# 1 + 2 = 3
# 3 + 4 = 7
# 5 + 6 = 11

Чтобы обобщить Nэлементы за раз:

N = 2
list(zip(*([iter(l)] * N)))
# [(1, 2), (3, 4), (5, 6)]
Квантовая Механика
источник
10
for (i, k) in zip(l[::2], l[1::2]):
    print i, "+", k, "=", i+k

zip(*iterable) возвращает кортеж со следующим элементом каждой итерации.

l[::2] возвращает 1-й, 3-й, 5-й и т. д. элемент списка: первое двоеточие указывает, что срез начинается с начала, потому что за ним нет номера, второе двоеточие необходимо только в том случае, если вы хотите шаг в срезе '(в данном случае 2).

l[1::2]делает то же самое, но начинается со второго элемента списков, поэтому возвращает 2-й, 4-й, 6-й и т. д. элемент исходного списка.

alexpinho98
источник
4
Этот ответ уже дал Маргус два года назад. stackoverflow.com/questions/5389507/…
cababunga
1
1 для объяснения того, как [number::number]работает синтаксис. полезно для тех, кто не часто использует python
Alby
2

Вы можете использовать пакет more_itertools .

import more_itertools

lst = range(1, 7)
for i, j in more_itertools.chunked(lst, 2):
    print(f'{i} + {j} = {i+j}')
Скотт Мин
источник
2

С распаковкой:

l = [1,2,3,4,5,6]
while l:
    i, k, *l = l
    print(str(i), '+', str(k), '=', str(i+k))
toaruScar
источник
Вот Это Да! Почему я не мог думать об этом :) Нужно просто разобраться со случаем, когда нет абсолютной пары (нечетные записи)
Саурав Кумар
1

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

Из документации по Python itertools :

from itertools import izip

def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return izip(a, b)

Или, в более общем плане:

from itertools import izip

def groupwise(iterable, n=2):
    "s -> (s0,s1,...,sn-1), (s1,s2,...,sn), (s2,s3,...,sn+1), ..."
    t = tee(iterable, n)
    for i in range(1, n):
        for j in range(0, i):
            next(t[i], None)
    return izip(*t)
Крис Малек
источник
1

Мне нужно разделить список на число и исправить это следующим образом.

l = [1,2,3,4,5,6]

def divideByN(data, n):
        return [data[i*n : (i+1)*n] for i in range(len(data)//n)]  

>>> print(divideByN(l,2))
[[1, 2], [3, 4], [5, 6]]

>>> print(divideByN(l,3))
[[1, 2, 3], [4, 5, 6]]
Али Каткар
источник
1

Есть много способов сделать это. Например:

lst = [1,2,3,4,5,6]
[(lst[i], lst[i+1]) for i,_ in enumerate(lst[:-1])]    
>>>[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]

[i for i in zip(*[iter(lst)]*2)]    
>>>[(1, 2), (3, 4), (5, 6)]
Бинит Бхагат
источник
0

Я подумал, что это хорошее место, чтобы поделиться моим обобщением этого для n> 2, которое является просто скользящим окном над повторяемым:

def sliding_window(iterable, n):
    its = [ itertools.islice(iter, i, None) 
            for i, iter
            in enumerate(itertools.tee(iterable, n)) ]                               

    return itertools.izip(*its)
Юваль
источник
0

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

for i,v in enumerate(items[:-1]):
        for u in items[i+1:]:
Офек Рон
источник
0

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

from typing import Iterator, Any, Iterable, TypeVar, Tuple

T_ = TypeVar('T_')
Pairs_Iter = Iterator[Tuple[T_, T_]]

def legs(iterable: Iterator[T_]) -> Pairs_Iter:
    begin = next(iterable)
    for end in iterable:
        yield begin, end
        begin = end
Влад Безден
источник
0

Упрощенный подход:

[(a[i],a[i+1]) for i in range(0,len(a),2)]

это полезно, если ваш массив - это, и вы хотите итерировать его по парам. Чтобы выполнить итерации по триплетам или более, просто измените команду шага «range», например:

[(a[i],a[i+1],a[i+2]) for i in range(0,len(a),3)]

(вам придется иметь дело с лишними значениями, если длина вашего массива и шаг не подходят)

mchrgr2000
источник
0
from itertools import tee
def pairwise(iterable):
    a = iter(iterable)
    for i in a:
        try:
            yield (i, next(a))
        except StopIteration:
            yield(i, None)
       
    

for i in pairwise([3, 7, 8, 9, 90, 900]):
    print(i)

Вывод:

(3, 7)
(8, 9)
(90, 900)
> 
Вишеш Мангла
источник
Вы можете выбрать любой наполнитель для None
Vishesh Mangla
-1

Здесь у нас может быть alt_elemметод, который может вписаться в ваш цикл for.

def alt_elem(list, index=2):
    for i, elem in enumerate(list, start=1):
        if not i % index:
           yield tuple(list[i-index:i])


a = range(10)
for index in [2, 3, 4]:
    print("With index: {0}".format(index))
    for i in alt_elem(a, index):
       print(i)

Вывод:

With index: 2
(0, 1)
(2, 3)
(4, 5)
(6, 7)
(8, 9)
With index: 3
(0, 1, 2)
(3, 4, 5)
(6, 7, 8)
With index: 4
(0, 1, 2, 3)
(4, 5, 6, 7)

Примечание. Приведенное выше решение может быть неэффективным, учитывая операции, выполняемые в func.

Санкет судаке
источник
-1
a_list = [1,2,3,4,5,6]
empty_list = [] 
for i in range(0,len(a_list),2):
   empty_list.append(a_list[i]+a_list[i+1])   
print(empty_list)
user391
источник