2010-07-27 26 views
16

Không an toàn để sửa đổi trình tự được lặp lại trong vòng lặp (điều này chỉ có thể xảy ra đối với các loại trình tự có thể thay đổi, chẳng hạn như danh sách). Nếu bạn cần phải sửa đổi danh sách mà bạn đang lặp lại (ví dụ, để nhân đôi các mục đã chọn), bạn phải lặp qua một bản sao. Ký hiệu lát làm cho điều này đặc biệt thuận tiện:Tại sao không an toàn để sửa đổi trình tự đang được lặp lại?

>>> for x in a[:]: # make a slice copy of the entire list 
    ... if len(x) > 6: a.insert(0, x) 
    ... 
    >>> a 
    ['defenestrate', 'cat', 'window', 'defenestrate'] 

tại sao nó không an toàn để chỉ làm for x in a ??

+2

Bạn đã nhận được kết quả gì khi thử? – SilentGhost

+0

Tôi tin rằng bên dưới trả lời câu hỏi của bạn. Nếu bạn không muốn sao chép toàn bộ danh sách, bạn cũng có thể duy trì danh sách các post-ops mà bạn có thể chạy sau khi lặp lại. –

Trả lời

13

Mà không quá kỹ thuật:

Nếu bạn đang lặp lại thông qua một chuỗi có thể thay đổi bằng Python và trình tự được thay đổi trong khi nó đang được lặp thông qua, nó không phải lúc nào cũng hoàn toàn rõ ràng những gì sẽ xảy ra. Nếu bạn chèn một phần tử trong chuỗi trong khi lặp qua nó, thì điều gì sẽ được coi là yếu tố "tiếp theo" hợp lý trong chuỗi? Nếu bạn xóa đối tượng tiếp theo thì sao?

Vì lý do này, lặp qua chuỗi có thể thay đổi trong khi bạn thay đổi, dẫn đến hành vi không xác định. Bất cứ điều gì có thể xảy ra, tùy thuộc vào chính xác như thế nào danh sách được thực hiện. :-)

1

Khi bạn sửa đổi bộ sưu tập mà bạn đang lặp qua trình lặp có thể hoạt động bất ngờ, ví dụ như bỏ lỡ các mục hoặc trả về cùng một mục hai lần.

Mã này lặp vô thời hạn khi tôi chạy nó:

>>> a = [ 'foo', 'bar', 'baz' ] 
>>> for x in a: 
... if x == 'bar': a.insert(0, 'oops') 

Điều này là do iterator sử dụng các chỉ số để theo dõi nó ở đâu trong danh sách. Thêm một mục ở đầu danh sách kết quả trong mục 'thanh' được trả về một lần nữa thay vì iterator tiến tới mục tiếp theo.

12

Đây là vấn đề phổ biến ở nhiều ngôn ngữ. Nếu bạn có một cấu trúc dữ liệu tuyến tính, và bạn đang lặp lại nó, một cái gì đó phải theo dõi bạn đang ở đâu trong cấu trúc. Nó có thể là một chỉ mục hiện tại, hoặc một con trỏ, nhưng đó là một loại ngón tay trỏ đến "vị trí hiện tại".

Nếu bạn sửa đổi danh sách trong khi lặp lại xảy ra, con trỏ đó có thể sẽ không chính xác.

Một vấn đề thường gặp là bạn loại bỏ mục dưới con trỏ, mọi thứ sẽ trượt xuống một, lần lặp tiếp theo của vòng lặp tăng dần con trỏ và bạn vô tình bỏ qua một mục.

Một số triển khai cấu trúc dữ liệu cung cấp khả năng xóa các mục trong khi lặp lại, nhưng hầu hết thì không.

+0

Trong 'for x in a:', là 'a' được đánh giá trên mỗi lần lặp? – haccks

+0

Không, nó được đánh giá một lần để có được một iterator, sau đó iterator được sử dụng cho vòng lặp. –

+0

OK. Bạn nói: * Một vấn đề phổ biến là bạn loại bỏ các mục dưới con trỏ, tất cả mọi thứ trượt xuống một, lặp đi lặp lại tiếp theo của vòng lặp increments con trỏ, và bạn đã vô tình bỏ qua một mục. *: Làm thế nào mà iterator sẽ được thông báo bất kỳ sửa đổi nào đối với 'a' nếu' a' chỉ được đánh giá một lần? – haccks

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