Lặp lại một danh sách dưới dạng cặp (hiện tại, tiếp theo) trong Python


131

Đôi khi tôi cần lặp lại một danh sách trong Python để xem phần tử "hiện tại" và phần tử "tiếp theo". Tôi có, cho đến bây giờ, đã làm như vậy với mã như:

for current, next in zip(the_list, the_list[1:]):
    # Do something

Điều này hoạt động và làm những gì tôi mong đợi, nhưng có một cách thành ngữ hay hiệu quả hơn để làm điều tương tự?


Kiểm tra câu trả lời của MizardX cho câu hỏi này . Nhưng tôi không nghĩ rằng giải pháp này là thành ngữ hơn của bạn.
Fábio Diniz


39
vì không có ai khác đề cập đến nó, tôi sẽ là người đó và chỉ ra rằng sử dụng cách nextnày sẽ che giấu một cách tích hợp.
gửi

@senderle Có lẽ đó là Python 2
Lối

2
@ thecoder16: nextcũng là một hàm tích hợp trong Python 2.
zondo

Câu trả lời:


131

Đây là một ví dụ có liên quan từ các tài liệu mô-đun itertools :

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

Đối với Python 2, bạn cần itertools.izipthay vì zip:

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

Cách thức hoạt động:

Đầu tiên, hai trình lặp song song abđược tạo ( tee()cuộc gọi), cả hai đều trỏ đến phần tử đầu tiên của lần lặp ban đầu. Vòng lặp thứ hai, bđược di chuyển 1 bước về phía trước (lệnh next(b, None))). Tại thời điểm này chỉ ađến s0 và btrỏ đến s1. Cả hai abcó thể đi qua bộ lặp ban đầu một cách độc lập - hàm izip lấy hai bộ lặp và tạo các cặp phần tử được trả về, tiến lên cả hai bộ lặp với cùng tốc độ.

Một cảnh báo: tee()hàm tạo ra hai vòng lặp có thể tiến độc lập với nhau, nhưng nó có chi phí. Nếu một trong các trình vòng lặp tiến xa hơn các trình lặp khác, thì tee() cần phải giữ các phần tử đã tiêu thụ trong bộ nhớ cho đến khi trình vòng lặp thứ hai quay lại chúng (nó không thể 'tua lại' trình vòng lặp gốc). Ở đây không có vấn đề gì vì một trình vòng lặp chỉ đi trước một bước so với trình lặp khác, nhưng nói chung, thật dễ dàng để sử dụng nhiều bộ nhớ theo cách này.

Và vì tee()có thể lấy một ntham số, nên nó cũng có thể được sử dụng cho nhiều hơn hai trình lặp song song:

def threes(iterator):
    "s -> (s0,s1,s2), (s1,s2,s3), (s2, s3,4), ..."
    a, b, c = itertools.tee(iterator, 3)
    next(b, None)
    next(c, None)
    next(c, None)
    return zip(a, b, c)

4
Mã ví dụ rất tuyệt ... nhưng, bạn có thể giải thích một chút về lý do tại sao nó hoạt động không? Giống như nói những gì "tee ()" và "next ()" đang làm ở đây.
John Mulder

@ John Mulder: Đã làm một bản tóm tắt ngắn.
Rafał Dowgird

9
zip(ł, ł[1:])ngắn hơn nhiều và pythonic
noɥʇʎԀʎzɐɹƆ 16/07/2016

2
@ noɥʇʎԀʎzɐɹƆ: Không, nó không hoạt động trên mọi lần lặp và tạo một bản sao không cần thiết khi được sử dụng trong danh sách. Sử dụng chức năng là pythonic.
Ry-

Hàm này được triển khai trong funcymô-đun funcy.pairwise:: funcy.readthedocs.io/en/urdy/seqs.html# Pairwise
ADR

30

Cuộn của riêng bạn!

def pairwise(iterable):
    it = iter(iterable)
    a = next(it, None)

    for b in it:
        yield (a, b)
        a = b

1
Đúng thứ tôi cần! Điều này đã được bất tử như một phương pháp trăn, hay chúng ta cần tiếp tục lăn?
uhoh

1
@uhoh: Vẫn chưa được như tôi biết!
Ry-

21

the_list[1:]thực sự tạo một bản sao của toàn bộ danh sách (không bao gồm phần tử đầu tiên của nó) và zip()tạo một danh sách các bộ dữ liệu ngay lập tức khi được gọi, trong tổng số ba bản sao của danh sách của bạn được tạo. Nếu danh sách của bạn rất lớn, bạn có thể thích

from itertools import izip, islice
for current_item, next_item in izip(the_list, islice(the_list, 1, None)):
    print(current_item, next_item)

mà không sao chép danh sách cả.


3
lưu ý rằng trong python 3.x izip bị chặn itertools và bạn nên sử dụng zip dựng sẵn
Xavier Combelle

1
Trên thực tế, không the_list[1:]chỉ tạo một đối tượng lát chứ không phải là một bản sao của gần như toàn bộ danh sách - vì vậy kỹ thuật của OP không hoàn toàn lãng phí như bạn tạo ra âm thanh.
martineau

3
Tôi nghĩ rằng [1:]tạo đối tượng lát (hoặc có thể là " 1:"), được truyền __slice__vào danh sách, sau đó trả về một bản sao chỉ chứa các phần tử được chọn. Một cách thành ngữ để sao chép danh sách là l_copy = l[:](mà tôi thấy xấu và không thể đọc được - thích l_copy = list(l))
dcrosta

4
@dcrosta: Không có __slice__phương pháp đặc biệt. the_list[1:]tương đương với the_list[slice(1, None)], mà lần lượt là tương đương với list.__getitem__(the_list, slice(1, None)).
Sven Marnach

4
@martineau: Bản sao được tạo bởi the_list[1:]chỉ là một bản sao nông, vì vậy nó chỉ bao gồm một con trỏ cho mỗi mục danh sách. Phần chiếm nhiều bộ nhớ hơn là zip()chính nó, bởi vì nó sẽ tạo một danh sách một tuplethể hiện cho mỗi mục danh sách, mỗi mục sẽ chứa hai con trỏ tới hai mục và một số thông tin bổ sung. Danh sách này sẽ tiêu thụ gấp chín lần dung lượng bộ nhớ mà bản sao gây ra [1:].
Sven Marnach

19

Tôi chỉ đưa ra điều này, tôi rất ngạc nhiên không ai nghĩ đến việc liệt kê ().

for (index, thing) in enumerate(the_list):
    if index < len(the_list):
        current, next_ = thing, the_list[index + 1]
        #do something

11
Trên thực tế, ifcũng có thể được gỡ bỏ nếu bạn sử dụng cắt lát:for (index, thing) in enumerate(the_list[:-1]): current, next_ = thing, the_list[index + 1]
tuổi thọ

2
Đây thực sự nên là câu trả lời, nó không phụ thuộc vào bất kỳ nhập khẩu thêm và hoạt động tuyệt vời.
jamescampbell

Mặc dù vậy, nó không hoạt động cho các iterables không thể lập chỉ mục nên nó không phải là một giải pháp chung chung.
Wim

14

Lặp lại theo chỉ mục có thể làm điều tương tự:

#!/usr/bin/python
the_list = [1, 2, 3, 4]
for i in xrange(len(the_list) - 1):
    current_item, next_item = the_list[i], the_list[i + 1]
    print(current_item, next_item)

Đầu ra:

(1, 2)
(2, 3)
(3, 4)

Câu trả lời của bạn là nhiều hơn trướchiện tại thay vì hiện tạitiếp theo , như trong câu hỏi. Tôi đã thực hiện một chỉnh sửa cải thiện ngữ nghĩa để iluôn luôn là chỉ số của yếu tố hiện tại.
Bengt

1

Đây là một Nhập khẩu đơn giản kể từ ngày 16 tháng 5 năm 2020

from more_itertools import pairwise
for current, next in pairwise(your_iterable):
  print(f'Current = {current}, next = {nxt}')

Tài liệu cho nhiều itertools Dưới mui xe, mã này giống như trong các câu trả lời khác, nhưng tôi thích nhập khẩu hơn khi có sẵn.

Nếu bạn chưa cài đặt thì: pip install more-itertools

Thí dụ

Chẳng hạn, nếu bạn có trình tự fftimenacci, bạn có thể tính tỷ lệ của các cặp tiếp theo là:

from more_itertools import pairwise
fib= [1,1,2,3,5,8,13]
for current, nxt in pairwise(fib):
    ratio=current/nxt
    print(f'Curent = {current}, next = {nxt}, ratio = {ratio} ')

0

Các cặp từ một danh sách sử dụng một sự hiểu biết danh sách

the_list = [1, 2, 3, 4]
pairs = [[the_list[i], the_list[i + 1]] for i in range(len(the_list) - 1)]
for [current_item, next_item] in pairs:
    print(current_item, next_item)

Đầu ra:

(1, 2)
(2, 3)
(3, 4)

0

Tôi thực sự ngạc nhiên không ai đề cập đến giải pháp chung ngắn hơn, đơn giản hơn và quan trọng nhất :

Con trăn 3:

from itertools import islice

def n_wise(iterable, n):
    return zip(*(islice(iterable, i, None) for i in range(n)))

Con trăn 2:

from itertools import izip, islice

def n_wise(iterable, n):
    return izip(*(islice(iterable, i, None) for i in xrange(n)))

Nó hoạt động cho phép lặp theo cặp bằng cách vượt qua n=2, nhưng có thể xử lý bất kỳ số nào cao hơn:

>>> for a, b in n_wise('Hello!', 2):
>>>     print(a, b)
H e
e l
l l
l o
o !

>>> for a, b, c, d in n_wise('Hello World!', 4):
>>>     print(a, b, c, d)
H e l l
e l l o
l l o
l o   W
o   W o
  W o r
W o r l
o r l d
r l d !

-2

Một giải pháp cơ bản:

def neighbors( list ):
  i = 0
  while i + 1 < len( list ):
    yield ( list[ i ], list[ i + 1 ] )
    i += 1

for ( x, y ) in neighbors( list ):
  print( x, y )

-2
code = '0016364ee0942aa7cc04a8189ef3'
# Getting the current and next item
print  [code[idx]+code[idx+1] for idx in range(len(code)-1)]
# Getting the pair
print  [code[idx*2]+code[idx*2+1] for idx in range(len(code)/2)]
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.