Phạm vi trong các vòng lặp Python 'cho'


177

Tôi không hỏi về các quy tắc phạm vi của Python; Tôi hiểu nói chung làm thế nào phạm vi hoạt động trong Python cho các vòng lặp. Câu hỏi của tôi là tại sao các quyết định thiết kế đã được thực hiện theo cách này. Ví dụ: (không có ý định chơi chữ):

for foo in xrange(10):
    bar = 2
print(foo, bar)

Ở trên sẽ in (9,2).

Điều này gây ấn tượng với tôi là kỳ lạ: 'foo' thực sự chỉ là kiểm soát vòng lặp và 'thanh' được xác định bên trong vòng lặp. Tôi có thể hiểu tại sao có thể cần thiết cho 'thanh' bên ngoài vòng lặp (nếu không, đối với các vòng lặp sẽ có chức năng rất hạn chế). Điều tôi không hiểu là tại sao cần phải điều khiển biến trong phạm vi sau khi thoát khỏi vòng lặp. Theo kinh nghiệm của tôi, nó chỉ đơn giản là làm tắc nghẽn không gian tên toàn cầu và khiến cho việc theo dõi các lỗi sẽ bị các phiên dịch viên trong các ngôn ngữ khác bắt gặp khó khăn hơn.


6
Nếu bạn không muốn forvòng lặp làm lộn xộn không gian tên toàn cầu của mình, hãy bọc nó trong một hàm. Đóng cửa thiên hạ!
jathanism

24
Trừ khi bạn đang chạy một vòng lặp trong không gian tên toàn cầu (không phổ biến), nó sẽ làm lộn xộn một không gian tên cục bộ .
Glenn Maynard

3
Nếu điều này không tồn tại, làm thế nào bạn sẽ tiếp tục xử lý sau tại thời điểm bạn rời khỏi vòng lặp? Chỉ cần xác định biến điều khiển trước vòng lặp?
endolith

9
@endolith Yeah ... Tại sao không yêu cầu điều đó?
Steven Lu

3
mọi người sẽ thích những gì họ đã từng làm Tôi muốn nói điều này làm tổn thương người viết mã trăn đã quen với loại điều này và phải trải qua một quá trình đau đớn khi chuyển sang một ngôn ngữ khác. Đối với phần còn lại của chúng tôi, đó là một lối tắt nhỏ gọn mà tôi cho là.
Steven Lu

Câu trả lời:


107

Câu trả lời dễ hiểu nhất là nó chỉ giữ cho ngữ pháp đơn giản, không phải là trở ngại cho việc áp dụng và nhiều người đã rất vui khi không phải phân biệt phạm vi mà tên thuộc về khi gán cho nó trong cấu trúc vòng lặp. Các biến không được khai báo trong một phạm vi, nó được ngụ ý bởi vị trí của các câu lệnh gán. Các globaltừ khóa tồn tại chỉ vì lý do này (để biểu thị nhượng được thực hiện tại một phạm vi toàn cầu).

Cập nhật

Đây là một cuộc thảo luận tốt về chủ đề này: http://mail.python.org/pipermail/python-ideas/2008-Oc/10/002109.html

Các đề xuất trước đây để tạo các biến vòng lặp cục bộ cho vòng lặp đã vấp phải vấn đề về mã hiện có dựa vào biến vòng lặp giữ giá trị của nó sau khi thoát khỏi vòng lặp và có vẻ như đây được coi là một tính năng mong muốn.

Nói tóm lại, có lẽ bạn có thể đổ lỗi cho cộng đồng Python: P


2
Làm thế nào ngữ pháp sẽ phức tạp hơn nếu phạm vi của biến cảm ứng được giới hạn trong cơ thể của vòng lặp? Một thay đổi như vậy sẽ được giới hạn trong phân tích ngữ nghĩa trong Python, chứ không phải ngữ pháp của nó.
Charles

6
Vòng lặp không phải là khối trong Python. Loại thay đổi hành vi này sẽ yêu cầu thay đổi ngữ pháp một cách cơ bản hoặc cung cấp một trường hợp đặc biệt. Toàn bộ khái niệm về một biến cảm ứng cũng không được thể hiện trong ngữ pháp hiện tại. Ngữ pháp cung cấp hợp đồng về cách thông dịch viên sẽ diễn giải. Quan điểm của tôi là tôi không thể thấy trước sự thay đổi trong hành vi này có thể được thực hiện mà không làm cho ngữ pháp trở nên phức tạp hơn. Đó là tất cả các moot vì hiệu ứng phụ của quyết định thiết kế đã trở thành một tính năng.
Jeremy Brown

1
Bài đăng này ở đây mail.python.org/pipermail/python-dev/2005-September/056677.html cung cấp thêm chi tiết về tốc độ và sự phức tạp mà ông Brown ám chỉ.
rajesh

61

Python không có các khối, cũng như một số ngôn ngữ khác (như C / C ++ hoặc Java). Do đó, đơn vị phạm vi trong Python là một hàm.


3
Tôi đang bối rối - điều gì ngăn Python phạm vi cho các vòng lặp giống như cách các hàm được phân chia?
chimeracoder

35
Không thực sự đúng, chỉ là ngữ pháp không trở nên điên rồ. ( Docs.python.org/reference/... ) "Một khối là một đoạn văn bản chương trình Python được thực hiện như một đơn vị Sau đây là các khối: một mô-đun, một cơ quan chức năng, và một định nghĩa lớp ...."
Jeremy Brown

1
@th trở lại, không có gì. Nó chỉ được coi là không cần thiết.
thói quen

@Jeremy Brown - thực sự. Lưu ý tốt.
atzz

6
@thebackhand - trong các ngôn ngữ có khối, forvòng lặp phạm vi là một phần mở rộng tự nhiên của một nguyên tắc chung. Trong Python, nó sẽ phải là một trường hợp đặc biệt và những trường hợp đặc biệt sẽ được tránh trừ khi chúng có những lợi ích hấp dẫn.
atzz

39

Một trường hợp thực sự hữu ích cho việc này là khi sử dụng enumeratevà bạn muốn tổng số cuối cùng:

for count, x in enumerate(someiterator, start=1):
    dosomething(count, x)
print "I did something {0} times".format(count)

Điều này có cần thiết không? Không. Nhưng, nó chắc chắn là thuận tiện.

Một điều khác cần lưu ý: trong Python 2, các biến trong việc hiểu danh sách cũng bị rò rỉ:

>>> [x**2 for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> x
9

Nhưng, điều tương tự không áp dụng cho Python 3.


4
Bạn có thể đã làm điều đó có lẽ trong elsemệnh đề, nghĩa là. else: print "I did something {0} times".format(count)- trước khi phạm vi cục bộ (không tồn tại trong Python) biến mất
Nas Banov

3
Chỉ ví dụ thứ hai không hoạt động trong Python 3, phải không? Việc đầu tiên vẫn làm? Ghi chú về lý do tại sao nó bị xóa khỏi Python 3?
endolith

7
để đếm, mục trong bảng liệt kê (a, start = 1): # chỉ số mặc định là từ 0
Tao Zhang

3
Ví dụ đầu tiên, thay vì là một trường hợp sử dụng tốt, có vẻ giống như bằng chứng cho thấy quy tắc phạm vi này là nguy hiểm và không nên dựa vào. Nếu someiteratortrống thì sao?
tối đa

1
@Nas Mặc dù một elsemệnh đề có thể được sử dụng trong trường hợp này, nhưng nó sẽ không hoạt động nói chung vì thân vòng lặp có thể breaksớm.
jamesdlin

2

Nếu bạn có một câu lệnh break trong vòng lặp (và muốn sử dụng giá trị lặp lại sau này, có lẽ để lấy lại, lập chỉ mục một cái gì đó hoặc đưa ra trạng thái), nó sẽ giúp bạn tiết kiệm một dòng mã và một phép gán, vì vậy sẽ thuận tiện.


1

Một trong những ảnh hưởng chính đối với Python là ABC , một ngôn ngữ được phát triển ở Hà Lan để dạy các khái niệm lập trình cho người mới bắt đầu. Người tạo ra Python, Guido van Rossum, đã làm việc trên ABC trong vài năm vào những năm 1980. Tôi hầu như không biết gì về ABC, nhưng vì nó dành cho người mới bắt đầu, tôi cho rằng nó phải có số lượng giới hạn, giống như BASIC ban đầu.


-1

Đối với người mới bắt đầu, nếu các biến là cục bộ của các vòng lặp, thì các vòng lặp đó sẽ vô dụng đối với hầu hết các chương trình trong thế giới thực.

Trong tình hình hiện tại:

# Sum the values 0..9
total = 0
for foo in xrange(10):
    total = total + foo
print total

sản lượng 45. Bây giờ, hãy xem xét cách phân công hoạt động trong Python. Nếu các biến vòng lặp là cục bộ nghiêm ngặt:

# Sum the values 0..9?
total = 0
for foo in xrange(10):
    # Create a new integer object with value "total + foo" and bind it to a new
    # loop-local variable named "total".
    total = total + foo
print total

năng suất 0, bởi vì totalbên trong vòng lặp sau khi gán không phải là biến giống như totalbên ngoài vòng lặp. Đây sẽ không phải là hành vi tối ưu hoặc dự kiến.


5
Không trả lời câu hỏi. OP đã hỏi về foo, không phải toàn bộ (hoặc thanh trong ví dụ của họ).
James Bradbury

6
@JamesBradbury totalfoovẫn sẽ có các ràng buộc vòng lặp cục bộ trong kịch bản của OP và logic là như nhau.
Kirk Strauser

2
OP: "Tôi có thể hiểu tại sao nó có thể là cần thiết cho 'bar' để có thể truy cập bên ngoài vòng lặp (chức năng khác, cho vòng sẽ rất hạn chế) Những gì tôi không hiểu là tại sao nó là cần thiết cho việc. Biến kiểm soát để duy trì trong phạm vi sau khi thoát khỏi vòng lặp. " (nhấn mạnh của tôi)
James Bradbury

2
@JamesBradbury Bạn có thể đúng, nhưng tôi đã trả lời điều này ba năm trước và bây giờ có lẽ không đáng để tranh luận.
Kirk Strauser
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.