Lặp lại mỗi hai yếu tố trong một danh sách


208

Làm thế nào để tôi thực hiện một forvòng lặp hoặc một danh sách hiểu để mỗi lần lặp lại cho tôi hai yếu tố?

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

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

Đầu ra:

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

2
Đối với cặp chồng chéo: stackoverflow.com/questions/5434891/ từ
user202729

Câu trả lời:


231

Bạn cần một pairwise()(hoặc grouped()) thực hiện.

Đối với 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)

Hoặc, nói chung hơn:

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)

Trong Python 3, bạn có thể thay thế izipbằng zip()hàm tích hợp và thả import.

Tất cả tín dụng cho martineau cho câu trả lời của anh ấy cho câu hỏi của tôi , tôi đã thấy điều này rất hiệu quả vì nó chỉ lặp lại một lần trong danh sách và không tạo ra bất kỳ danh sách không cần thiết nào trong quy trình.

Lưu ý : Điều này không nên nhầm lẫn với pairwisecông thức trong itertoolstài liệu riêng của Python , mang lại s -> (s0, s1), (s1, s2), (s2, s3), ..., như được chỉ ra bởi @lazyr trong các bình luận.

Một bổ sung nhỏ cho những ai muốn kiểm tra kiểu với mypy trên 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
Đừng nhầm lẫn với chức năng cặp đôi được đề xuất trong phần công thức nấu ăn itertools , mang lạis -> (s0,s1), (s1,s2), (s2, s3), ...
Lauritz V. Thaulow

1
Nó làm một điều khác biệt. Phiên bản của bạn chỉ mang lại một nửa số lượng cặp so với itertoolschức năng công thức có cùng tên. Tất nhiên là của bạn nhanh hơn ...
Sven Marnach

Huh? Chức năng của bạn và chức năng tôi đề cập để làm những việc khác nhau, và đó là điểm nhận xét của tôi.
Lauritz V. Thaulow

5
HÃY CẨN THẬN! Sử dụng các hàm này khiến bạn có nguy cơ không lặp lại các yếu tố cuối cùng của một lần lặp. Ví dụ: danh sách (được nhóm ([1,2,3], 2)) >>> [(1, 2)] .. khi bạn mong đợi [(1,2), (3,)]
egafni

4
@ Erik49: Trong trường hợp được chỉ định trong câu hỏi, sẽ không có ý nghĩa gì khi có một bộ 'không hoàn chỉnh'. Nếu bạn muốn bao gồm một bộ dữ liệu không đầy đủ, bạn có thể sử dụng izip_longest()thay vì izip(). Vd: list(izip_longest(*[iter([1, 2, 3])]*2, fillvalue=0))-> [(1, 2), (3, 0)]. Hi vọng điêu nay co ich.
Johnsyweb

191

Vâng, bạn cần bộ 2 yếu tố, vì vậy

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)

Ở đâu:

  • data[0::2] có nghĩa là tạo tập hợp con của các phần tử (index % 2 == 0)
  • zip(x,y) tạo một bộ sưu tập tuple từ các tập hợp x và y cùng các phần tử chỉ mục.

8
Điều này cũng có thể được mở rộng trong trường hợp cần nhiều hơn hai yếu tố. Đối với ví dụfor i, j, k in zip(data[0::3], data[1::3], data[2::3]):
lifebalance

19
Vì vậy, sạch sẽ hơn nhiều so với nhập khẩu và xác định một chức năng!
kmarsh

7
@kmarsh: Nhưng điều này chỉ hoạt động trên các chuỗi, chức năng hoạt động trên bất kỳ lần lặp nào; và điều này sử dụng không gian thêm O (N), hàm không; mặt khác, điều này thường nhanh hơn. Có những lý do tốt để chọn cái này hay cái khác; sợ importkhông phải là một trong số họ.
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
Điều này không hoạt động trên Python-3.6.0 nhưng vẫn hoạt động trên Python-2.7.10
Hamid Rohani

6
@ HamidRohani ziptrả về một zipđối tượng trong Python 3, không thể đăng ký được. Trước tiên, nó cần được chuyển đổi thành một chuỗi ( list, tuplev.v.), nhưng "không hoạt động" là một chút kéo dài.
vaultah

58

Một giải pháp đơn giản.

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

cho i trong phạm vi (0, len (l), 2):
    in str (l [i]), '+', str (l [i + 1]), '=', str (l [i] + l [i + 1])

1
Điều gì xảy ra nếu danh sách của bạn không đồng đều và bạn muốn chỉ hiển thị số cuối cùng?
Hans de Jong

@HansdeJong không hiểu bạn. Hãy giải thích thêm một chút.
taskinoor

2
Cảm ơn. Tôi đã tìm ra cách để làm điều đó. Vấn đề là nếu bạn có một danh sách thậm chí không có số lượng trong đó, nó sẽ bị lỗi chỉ mục. Đã giải quyết nó bằng một thử: ngoại trừ:
Hans de Jong

Hoặc ((l[i], l[i+1])for i in range(0, len(l), 2))đối với một máy phát điện, có thể dễ dàng sửa đổi cho các bộ dữ liệu dài hơn.
Basel Shishani

44

Mặc dù tất cả các câu trả lời sử dụng zipđều đúng, tôi thấy rằng việc tự thực hiện chức năng dẫn đến mã dễ đọc hơn:

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

Phần it = iter(it)đảm bảo rằng đó itthực sự là một trình vòng lặp, không chỉ là một vòng lặp. Nếu itđã là một trình vòng lặp, dòng này là không có.

Sử dụng:

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

2
Giải pháp này cho phép khái quát hóa kích thước của các bộ dữ liệu> 2
guilloptero

1
Giải pháp này cũng hoạt động nếu itchỉ là một trình vòng lặp và không phải là một vòng lặp. Các giải pháp khác dường như dựa vào khả năng tạo hai vòng lặp độc lập cho chuỗi.
skyking

Tôi tìm thấy cách tiếp cận này tại stackoverflow.com/a/16815056/2480481 trước khi thấy câu trả lời này. Sạch hơn, dễ dàng hơn so với giao dịch với zip ().
m3nda

2
Tôi thích điều đó cho phép tránh sử dụng bộ nhớ gấp ba lần như câu trả lời được chấp nhận.
Kentzo

Điều này không hoạt động tốt với forcác vòng lặp trong Python 3.5+ do PEP 479 , thay thế bất kỳ StopIterationđược nâng lên trong một trình tạo bằng a RuntimeError.
sidney

27

Tôi hy vọng đây sẽ là cách làm thanh lịch hơn nữa.

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

[(1, 2), (3, 4), (5, 6)]

18

Trong trường hợp bạn quan tâm đến hiệu suất, tôi đã thực hiện một điểm chuẩn nhỏ (sử dụng thư viện của mình simple_benchmark) để so sánh hiệu suất của các giải pháp và tôi đã bao gồm một chức năng từ một trong các gói của mình: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)

nhập mô tả hình ảnh ở đây

Vì vậy, nếu bạn muốn giải pháp nhanh nhất mà không cần phụ thuộc bên ngoài, có lẽ bạn chỉ nên sử dụng phương pháp do Johnysweb đưa ra (tại thời điểm viết nó là câu trả lời được chấp nhận và được chấp nhận nhiều nhất).

Nếu bạn không nhớ phụ thuộc bổ sung thì các groupertừ iteration_utilitiescó lẽ sẽ nhanh hơn một chút.

Suy nghĩ thêm

Một số cách tiếp cận có một số hạn chế, chưa được thảo luận ở đây.

Ví dụ: một vài giải pháp chỉ hoạt động cho các chuỗi (đó là danh sách, chuỗi, v.v.), ví dụ: các giải pháp Margus / pyanon / taskinoor sử dụng lập chỉ mục trong khi các giải pháp khác hoạt động trên bất kỳ iterable nào (đó là trình tự trình tạo, trình lặp) như Johnysweb / mic_e / giải pháp của tôi.

Sau đó, Johnysweb cũng cung cấp một giải pháp hoạt động cho các kích thước khác ngoài 2 trong khi các câu trả lời khác thì không (không sao, iteration_utilities.groupercũng cho phép đặt số lượng phần tử thành "nhóm").

Sau đó, cũng có câu hỏi về những gì sẽ xảy ra nếu có một số phần tử lẻ trong danh sách. Các mặt hàng còn lại có nên được miễn nhiệm? Danh sách nên được đệm để làm cho nó thậm chí có kích thước? Các mặt hàng còn lại có nên được trả lại như là duy nhất? Câu trả lời khác không đề cập trực tiếp đến điểm này, tuy nhiên nếu tôi không bỏ qua bất cứ điều gì thì tất cả đều tuân theo cách tiếp cận rằng mục còn lại sẽ bị loại bỏ (ngoại trừ câu trả lời của người làm nhiệm vụ - điều đó thực sự sẽ gây ra Ngoại lệ).

Với grouperbạn có thể quyết định những gì bạn muốn làm:

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

Sử dụng các lệnh zipitercùng nhau:

Tôi thấy giải pháp này sử dụng iterkhá thanh lịch:

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

Mà tôi tìm thấy trong tài liệu zip 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

Để khái quát cho Ncác yếu tố tại một thời điểm:

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) trả về một tuple với phần tử tiếp theo của mỗi lần lặp.

l[::2] trả về phần tử thứ 1, thứ 3, thứ 5, v.v. của danh sách: dấu hai chấm đầu tiên chỉ ra rằng lát cắt bắt đầu từ đầu bởi vì không có số nào phía sau nó, dấu hai chấm thứ hai chỉ cần nếu bạn muốn một bước 'trong lát cắt '(trong trường hợp này là 2).

l[1::2]thực hiện tương tự nhưng bắt đầu trong phần tử thứ hai của danh sách để nó trả về phần tử thứ 2, thứ 4, thứ 6, v.v. của danh sách gốc .


4
Câu trả lời này đã được Margus đưa ra hai năm trước. stackoverflow.com/questions/5389507/
trộm

1
1 để giải thích cách [number::number]hoạt động của cú pháp. hữu ích cho những người không sử dụng trăn thường xuyên
Alby 26/12/13

2

Với việc giải nén:

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

Ồ Tại sao tôi không thể nghĩ về điều này :) Bạn cần xử lý trường hợp không có cặp tuyệt đối (mục lẻ)
Saurav Kumar

1

Đối với bất kỳ ai, điều này có thể giúp ích, đây là một giải pháp cho một vấn đề tương tự nhưng với các cặp chồng chéo (thay vì các cặp loại trừ lẫn nhau).

Từ tài liệu itertools của 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)

Hoặc, nói chung hơn:

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

bạn có thể sử dụng gói more_itertools .

import more_itertools

lst = range(1, 7)
for i, j in more_itertools.chunked(lst, 2):
    print(f'{i} + {j} = {i+j}')

1

Tôi cần chia một danh sách cho một số và cố định như thế này.

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

Có nhiều cách để làm điều đó. Ví dụ:

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

Tôi nghĩ rằng đây là một nơi tốt để chia sẻ khái quát của tôi về điều này cho n> 2, đây chỉ là một cửa sổ trượt trên một vòng lặp:

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

Tiêu đề của câu hỏi này là sai lệch, bạn dường như đang tìm kiếm các cặp liên tiếp, nhưng nếu bạn muốn lặp lại tập hợp của tất cả các cặp có thể hơn thì điều này sẽ hoạt động:

for i,v in enumerate(items[:-1]):
        for u in items[i+1:]:

0

Sử dụng cách gõ để bạn có thể xác minh dữ liệu bằng công cụ phân tích tĩnh 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

Một cách tiếp cận đơn giản:

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

Điều này rất hữu ích nếu mảng của bạn là một và bạn muốn lặp lại theo từng cặp. Để lặp lại trên bộ ba hoặc nhiều hơn, chỉ cần thay đổi lệnh bước "phạm vi", ví dụ:

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

(bạn phải xử lý các giá trị vượt quá nếu độ dài mảng và bước không phù hợp)


-1

Ở đây chúng ta có thể có alt_elemphương thức có thể phù hợp với vòng lặp for của bạn.

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)

Đầu ra:

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)

Lưu ý: Giải pháp trên có thể không hiệu quả khi xem xét các hoạt động được thực hiện trong 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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.