s = [1,2,3,4,5,6,7,8,9]
n = 3
zip(*[iter(s)]*n) # returns [(1,2,3),(4,5,6),(7,8,9)]
Làm thế nào để zip(*[iter(s)]*n)
làm việc? Nó sẽ trông như thế nào nếu nó được viết với nhiều mã dài dòng hơn?
s = [1,2,3,4,5,6,7,8,9]
n = 3
zip(*[iter(s)]*n) # returns [(1,2,3),(4,5,6),(7,8,9)]
Làm thế nào để zip(*[iter(s)]*n)
làm việc? Nó sẽ trông như thế nào nếu nó được viết với nhiều mã dài dòng hơn?
Câu trả lời:
iter()
là một trình lặp trên một chuỗi. [x] * n
tạo ra một danh sách chứa n
số lượng x
, tức là một danh sách độ dài n
, vị trí của mỗi phần tử x
. *arg
giải nén một chuỗi thành các đối số cho một lệnh gọi hàm. Do đó, bạn đang chuyển cùng một trình lặp 3 lần đến zip()
và nó kéo một mục từ trình lặp mỗi lần.
x = iter([1,2,3,4,5,6,7,8,9])
print zip(x, x, x)
yield
s (= return
s) một mục, bạn có thể hình dung mục này là "đã tiêu thụ". Vì vậy, lần tiếp theo trình lặp được gọi, nó sẽ tạo ra mục "chưa cộng dồn" tiếp theo.
Các câu trả lời và nhận xét tuyệt vời khác giải thích rõ vai trò của đối số giải nén và zip () .
Như Ignacio và ujukatzel nói, bạn chuyển tới zip()
ba tham chiếu đến cùng một trình vòng lặp và zip()
tạo 3 bộ số nguyên — theo thứ tự — từ mỗi tham chiếu đến trình vòng lặp:
1,2,3,4,5,6,7,8,9 1,2,3,4,5,6,7,8,9 1,2,3,4,5,6,7,8,9
^ ^ ^
^ ^ ^
^ ^ ^
Và vì bạn yêu cầu một mẫu mã dài dòng hơn:
chunk_size = 3
L = [1,2,3,4,5,6,7,8,9]
# iterate over L in steps of 3
for start in range(0,len(L),chunk_size): # xrange() in 2.x; range() in 3.x
end = start + chunk_size
print L[start:end] # three-item chunks
Theo các giá trị của start
và end
:
[0:3) #[1,2,3]
[3:6) #[4,5,6]
[6:9) #[7,8,9]
FWIW, bạn có thể nhận được kết quả tương tự với map()
đối số ban đầu là None
:
>>> map(None,*[iter(s)]*3)
[(1, 2, 3), (4, 5, 6), (7, 8, 9)]
Để biết thêm về zip()
và map()
: http://muffinresearch.co.uk/archives/2007/10/16/python-transposing-lists-with-map-and-zip/
Tôi nghĩ một điều bị bỏ sót trong tất cả các câu trả lời (có thể rõ ràng đối với những người quen thuộc với trình lặp) nhưng không quá rõ ràng đối với những người khác là -
Vì chúng ta có cùng một trình vòng lặp, nó sẽ bị tiêu thụ và các phần tử còn lại được sử dụng bởi zip. Vì vậy, nếu chúng ta chỉ đơn giản sử dụng danh sách chứ không phải iter ví dụ.
l = range(9)
zip(*([l]*3)) # note: not an iter here, the lists are not emptied as we iterate
# output
[(0, 0, 0), (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, 5), (6, 6, 6), (7, 7, 7), (8, 8, 8)]
Sử dụng trình vòng lặp, bật các giá trị và chỉ giữ nguyên khả dụng, vì vậy, đối với zip khi 0 được sử dụng, 1 có sẵn và sau đó là 2, v.v. Một điều rất tinh tế, nhưng khá thông minh !!!
iter(s)
trả về một trình lặp cho s.
[iter(s)]*n
tạo danh sách n lần cùng một trình lặp cho s.
Vì vậy, khi thực hiện zip(*[iter(s)]*n)
, nó trích xuất một mục từ cả ba trình vòng lặp từ danh sách theo thứ tự. Vì tất cả các trình vòng lặp là cùng một đối tượng, nó chỉ nhóm danh sách thành nhiều phần n
.
Một lời khuyên cho việc sử dụng zip theo cách này. Nó sẽ cắt ngắn danh sách của bạn nếu độ dài của nó không chia đều. Để giải quyết vấn đề này, bạn có thể sử dụng itertools.izip_longest nếu bạn có thể chấp nhận các giá trị điền. Hoặc bạn có thể sử dụng một cái gì đó như thế này:
def n_split(iterable, n):
num_extra = len(iterable) % n
zipped = zip(*[iter(iterable)] * n)
return zipped if not num_extra else zipped + [iterable[-num_extra:], ]
Sử dụng:
for ints in n_split(range(1,12), 3):
print ', '.join([str(i) for i in ints])
Bản in:
1, 2, 3
4, 5, 6
7, 8, 9
10, 11
itertools
công thức nấu ăn: docs.python.org/2/library/itertools.html#recipes grouper
. Không cần phải phát minh lại bánh xe
Có thể dễ dàng hơn để xem những gì đang xảy ra trong trình thông dịch python hoặc ipython
với n = 2
:
In [35]: [iter("ABCDEFGH")]*2
Out[35]: [<iterator at 0x6be4128>, <iterator at 0x6be4128>]
Vì vậy, chúng tôi có một danh sách hai trình vòng lặp đang trỏ đến cùng một đối tượng trình vòng lặp. Hãy nhớ rằng iter
trên một đối tượng trả về một đối tượng trình lặp và trong trường hợp này, nó là cùng một trình lặp hai lần do *2
đường cú pháp python. Trình lặp cũng chỉ chạy một lần.
Hơn nữa, zip
lấy bất kỳ số lượng lặp lại nào ( chuỗi là lặp lại ) và tạo bộ từ phần tử thứ i của mỗi chuỗi đầu vào. Vì cả hai trình vòng lặp đều giống nhau trong trường hợp của chúng tôi, zip di chuyển cùng một trình vòng lặp hai lần cho mỗi bộ đầu ra 2 phần tử.
In [41]: help(zip)
Help on built-in function zip in module __builtin__:
zip(...)
zip(seq1 [, seq2 [...]]) -> [(seq1[0], seq2[0] ...), (...)]
Return a list of tuples, where each tuple contains the i-th element
from each of the argument sequences. The returned list is truncated
in length to the length of the shortest argument sequence.
Các giải nén ( *
) điều hành đảm bảo rằng các vòng lặp chạy đến kiệt sức mà trong trường hợp này là cho đến khi không có đủ đầu vào để tạo ra một tuple 2 phần tử.
Điều này có thể được mở rộng đến bất kỳ giá trị nào n
và zip(*[iter(s)]*n)
hoạt động như được mô tả.
*
chỉ là sự tiện lợi để sao chép một đối tượng. Hãy thử nó với vô hướng và sau đó với danh sách. Cũng cố gắng print(*zip(*[iter("ABCDEFG")]*2))
vs print(*zip(*[iter("ABCDEFG"), iter("ABCDEFG")]))
. Sau đó, bắt đầu chia nhỏ cả hai thành các bước nhỏ hơn để xem các đối tượng thực sự của trình lặp trong hai câu lệnh là gì.