2015-05-12 19 views
16

Khi gói một bộ lặp (nội bộ) thường phải định tuyến lại phương thức __iter__ đến vòng lặp cơ bản. Hãy xem xét ví dụ sau:"yield from iterable" vs "return iter (iterable)"

class FancyNewClass(collections.Iterable): 
    def __init__(self): 
     self._internal_iterable = [1,2,3,4,5] 

    # ... 

    # variant A 
    def __iter__(self): 
     return iter(self._internal_iterable) 

    # variant B 
    def __iter__(self): 
     yield from self._internal_iterable 

Có sự khác biệt đáng kể nào giữa biến thể A và B không? Biến thể Trả về đối tượng trình lặp đã được truy vấn qua iter() từ nội bộ có thể lặp lại. Biến thể B trả về một đối tượng máy phát điện trả về các giá trị từ vòng lặp nội bộ. Là một hay khác thích hợp hơn vì một lý do nào đó? Trong collections.abc phiên bản yield from được sử dụng. Biến thể return iter() là mẫu mà tôi đã sử dụng cho đến bây giờ.

Trả lời

11

Sự khác biệt đáng kể duy nhất là điều gì xảy ra khi ngoại lệ được nâng lên từ trong vòng lặp có thể lặp lại. Sử dụng return iter()FancyNewClass của bạn sẽ không xuất hiện trên traceback ngoại lệ, trong khi với yield from nó sẽ. Nó thường là một điều tốt để có càng nhiều thông tin trên traceback càng tốt, mặc dù có thể có những tình huống mà bạn muốn ẩn wrapper của bạn.

khác biệt:

  • return iter có để nạp tên iter từ globals - đây là khả năng chậm (mặc dù khó có khả năng ảnh hưởng đáng kể hiệu suất) và có thể được điều sai lầm với (mặc dù bất cứ ai ghi đè globals như thế xứng đáng những gì họ nhận được).

  • Với yield from bạn có thể chèn các biểu thức yield khác trước và sau (mặc dù bạn có thể sử dụng đồng đều itertools.chain).

  • Như đã trình bày, hình thức yield from loại bỏ bất kỳ giá trị trả về máy phát điện (tức là raise StopException(value). Bạn có thể khắc phục điều này bằng cách viết thay vì return (yield from iterator).

Dưới đây là một thử nghiệm so sánh tháo gỡ của hai cách tiếp cận và cũng cho thấy ngoại lệ tracebacks: http://ideone.com/1YVcSe

Sử dụng return iter():

3   0 LOAD_GLOBAL    0 (iter) 
       3 LOAD_FAST    0 (it) 
       6 CALL_FUNCTION   1 (1 positional, 0 keyword pair) 
       9 RETURN_VALUE 
Traceback (most recent call last): 
    File "./prog.py", line 12, in test 
    File "./prog.py", line 10, in i 
RuntimeError 

Sử dụng return (yield from):

5   0 LOAD_FAST    0 (it) 
       3 GET_ITER 
       4 LOAD_CONST    0 (None) 
       7 YIELD_FROM 
       8 RETURN_VALUE 
Traceback (most recent call last): 
    File "./prog.py", line 12, in test 
    File "./prog.py", line 5, in bar 
    File "./prog.py", line 10, in i 
RuntimeError 
+0

Giả sử mục tiêu là để chỉ đơn giản là chuyển/lộ tiềm ẩn iterable, tức là không có sửa đổi/tiêm các mặt hàng khác, tôi nghĩ mình sẽ gắn bó với 'iter()' vì: 1) nếu tôi không thêm bất kỳ hành vi bổ sung, tôi không cần nó để xuất hiện trên traceback ngoại lệ (tôi nghĩ?). 2) để tránh sử dụng toàn cục 'iter()' tôi có thể gọi 'return internal_iterable .__ iter __()'. Về điểm thứ ba bạn đã nêu ra, tôi sẽ phải suy nghĩ về điều đó có nghĩa là gì. – PeterE

Các vấn đề liên quan