Перебор всех двух элементов в списке - PullRequest
173 голосов
/ 22 марта 2011

Как сделать цикл 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

Ответы [ 18 ]

206 голосов
/ 22 марта 2011

Вам нужна реализация 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 в собственной документации Python itertools документации , что дает 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)
158 голосов
/ 22 марта 2011

Ну, вам нужен кортеж из 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.
64 голосов
/ 22 марта 2011
>>> 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']
55 голосов
/ 22 марта 2011

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

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

for i in range(0, len(l), 2):
    print str(l[i]), '+', str(l[i + 1]), '=', str(l[i] + l[i + 1])
38 голосов
/ 24 мая 2015

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

def pairwise(it):
    it = iter(it)
    while True:
        yield next(it), next(it)

Часть it = iter(it) гарантирует, что it на самом деле является итераторомне просто повторяемый.Если it уже является итератором, эта строка не используется.

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

for a, b in pairwise([0, 1, 2, 3, 4, 5]):
    print(a + b)
14 голосов
/ 19 августа 2016

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

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

[(1, 2), (3, 4), (5, 6)]
12 голосов
/ 10 апреля 2018

Если вам интересна производительность, я провел небольшой тест (используя мою библиотеку 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)

enter image description here

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

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

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

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

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

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

Тогда возникает также вопрос о том, что должно произойти, если в списке нечетное количество элементов.Оставшийся предмет должен быть уволен?Должен ли список быть дополнен, чтобы сделать его еще меньше?Оставшийся предмет должен быть возвращен как один?Другой ответ не касается этого вопроса напрямую, однако, если я ничего не пропустил, все они следуют подходу, согласно которому оставшийся элемент должен быть отклонен (за исключением ответа 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)]
9 голосов
/ 19 января 2018

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

Я считаю, что это решение, использующее iter, довольно элегантно:

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

Что я нашел в Документация по Python 3 .

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)]
9 голосов
/ 30 мая 2013
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-й и т. д. элемент оригинального списка.

2 голосов
/ 25 февраля 2017

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

Из 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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...