Ітерація над кожні два елементи списку


Відповіді:


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)

16
Не плутати з парною функцією, запропонованою в розділі рецептів itertools , який дає результатs -> (s0,s1), (s1,s2), (s2, s3), ...
Лауріц В. Таулов

1
Це робить іншу річ. Ваша версія дає лише половину кількості пар порівняно з однойменною itertoolsфункцією рецепта. Звичайно, ваш швидше ...
Свен Марнах

Так? Ваша функція та функція, про яку я посилався, робили різні речі, і це було моїм коментарем.
Лауріц В. Таулов

5
БУДЬ ОБЕРЕЖНИЙ! За допомогою цих функцій ви ризикуєте не повторювати останні елементи, які можна повторити. Приклад: список (згрупований ([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]):
життєвий баланс

19
Настільки чистіше, ніж використання імпорту та визначення функції!
кмарш

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']

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):
    друку str (l [i]), '+', str (l [i + 1]), '=', str (l [i] + l [i + 1])

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

The it = iter(it) частині гарантує , що itнасправді итератор, а не просто ітерація. Якщо itвже є ітератором, цей рядок є неопераційним.

Використання:

for a, b in pairwise([0, 1, 2, 3, 4, 5]):
    print(a + b)

2
Це рішення дозволяє узагальнити розмір кортежів> 2
гільйоптеро

1
Це рішення також працює, якщо itє лише ітератором, а не ітерабельним. Інші рішення, схоже, покладаються на можливість створення двох незалежних ітераторів для послідовності.
снові

Я знайшов такий підхід на сайті stackoverflow.com/a/16815056/2480481, перш ніж побачити цю відповідь. Чистіше, простіше, ніж працювати з zip ().
м3нда

2
Мені подобається, що це дозволяє уникнути утроєного використання пам'яті як прийнятої відповіді.
Кенцо

Це не добре працює з forциклами в Python 3.5+ за рахунок PEP 479 , який замінює будь-яке StopIterationпідняте в генераторі на RuntimeError.
sidney

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також дозволяє встановити кількість елементів на "групувати").

Тоді також виникає питання про те, що має статися, якщо в списку є непарна кількість елементів. Чи повинен бути залишений пункт, що залишився? Чи має бути список прокладений, щоб зробити його рівним? Чи повинен бути повернутий решту як єдиний? Інша відповідь не стосується цього питання безпосередньо, однак, якщо я нічого не переглянув, всі вони дотримуються підходу до відмови, що залишився (крім випадків відповіді задач - це фактично призведе до винятку).

З 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)]

12

Використовуйте команди 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)]

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 та ін. початкового списку.


4
Цю відповідь Маргус уже дав два роки тому. stackoverflow.com/questions/5389507 / ...
cababunga

1
1 для пояснення того, як [number::number]працює синтаксис. корисно для тих, хто не використовує python часто
Alby

2

З розпакуванням:

l = [1,2,3,4,5,6]
while l:
    i, k, *l = l
    print(str(i), '+', str(k), '=', str(i+k))

Оце Так! Чому я не міг про це подумати :) Вам потрібно просто розібратися у випадку, коли немає абсолютної пари (непарні записи)
Саурав Кумар,

1

Для когось це може допомогти, тут є рішення подібної проблеми, але з парами, що перекриваються (замість взаємовиключних пар).

З документації на itertools Python :

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)]

це корисно, якщо ваш масив є a і ви хочете повторити його парами. Для повторення трійки або більше просто змініть крок команди "діапазон", наприклад:

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

(вам доведеться мати справу із зайвими значеннями, якщо довжина масиву та крок не підходять)


-1

Тут ми можемо мати alt_elemметод, який може вміститися у вашому циклі.

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)

Примітка: Наведене рішення може бути неефективним, враховуючи операції, що виконуються у функції.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.