2017-01-10 15 views
19

Với đoạn mã sau:biểu phát Nested cư xử bất ngờ

A = [1, 2] 
B = [-2, -1] 
C = [-1, 2] 
D = [0, 2] 

ab = (a + b for a in A for b in B) 
cd = (c + d for c in C for d in D) 
abcd = (e_ab + e_cd for e_ab in ab for e_cd in cd) 

Các len(abcd) dự kiến ​​sẽ được 16, nhưng nó thực sự là 4. Nếu tôi sử dụng một sự hiểu biết danh sách thay vào đó, vấn đề sẽ biến mất. Tại sao vậy?

Trả lời

26

Bạn chỉ có thể đi xe tàu điện một lần, sau khi đến đích, không còn phải cưỡi. Trong trường hợp của bạn, máy phát điện cd bị cạn kiệt và sau đó không thể lặp lại được.

list đối tượng, mặt khác, tạo ra một đối tượng iterator riêng mỗi khi bạn gọi iter trên chúng (mà for loop ngầm làm cho bạn):

print(iter([1, 2, 3])) 
# <list_iterator at 0x7f18495d4c88> 

và tạo ra một iterator tươi bạn co thể sử dụng. Điều này xảy ra bất kỳ lúc nàoiter được gọi trên đó; kể từ khi một đối tượng mới được tạo ra mỗi lần, bạn có thể đi qua danh sách nhiều lần. Nhiều chuyến đi!

Nói tóm lại, nếu bạn chỉ thay đổi cd trở thành một danh sách (nói chung, các đối tượng đó sẽ được lặp qua nhiều lần):

ab = (a + b for a in A for b in B) 
cd = [c + d for c in C for d in D] # list-comp instead 

nó sẽ mang lại kết quả mong muốn bằng cách tạo ra iterator tươi các đối tượng từ cd cho mọi phần tử trong ab:

abcd = (e_ab + e_cd for e_ab in ab for e_cd in cd) 
print(len(list(abcd))) 
# 16 

tất nhiên bạn có thể đạt được điều này bằng cách sử dụng từ ngày 01 quá nhưng, đó là vượt quá điểm của lý do tại sao điều này xảy ra.

14

Tôi nghĩ rằng điều này là do bạn chỉ có thể lặp qua máy phát điện một lần. Vì vậy, sau khi bạn lặp đi lặp lại kỹ lưỡng e_cd lần đầu tiên điều này sẽ không tạo ra bất cứ điều gì trên một vòng lặp khác của chu kỳ bên ngoài.

12

Khi trình tạo không có giá trị nào khác để trả về, nó sẽ tạo ra ngoại lệ StopIteration. Đây là cách họ báo hiệu rằng họ đã làm xong. Vì không có cách tích hợp để thiết lập lại máy phát, khi bạn tạo trình tạo nhiều giai đoạn từ máy phát, nó sẽ dừng ở lần đầu tiên gặp phải StopIteration thay vì khiến máy phát con lặp lại như xảy ra với các đối tượng giống như danh sách.

itertools.product() có thể tạo ra kết quả mong muốn (repl.it here):

import itertools 

A = [1, 2] 
B = [-2, -1] 
C = [-1, 2] 
D = [0, 2] 

ab = (a + b for a in A for b in B) 
cd = (c + d for c in C for d in D) 
abcd = (e_ab + e_cd for e_ab, e_cd in itertools.product(ab,cd)) 
+0

Điều này cũng có thể được viết là 'bản đồ (tổng, itertools.product (A, B, C, D))'. – deltab