Określanie zakresu w pętlach „for” w języku Python

177

Nie pytam o reguły zakresu Pythona; Ogólnie rozumiem, jak działa określanie zakresu w Pythonie dla pętli. Moje pytanie brzmi, dlaczego decyzje projektowe były podejmowane w ten sposób. Na przykład (gra słów nie przeznaczona):

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

Powyższe wydrukuje (9,2).

Wydaje mi się to dziwne: „foo” tak naprawdę kontroluje pętlę, a „bar” został zdefiniowany wewnątrz pętli. Rozumiem, dlaczego może być konieczne, aby „bar” był dostępny poza pętlą (w przeciwnym razie pętle miałyby bardzo ograniczoną funkcjonalność). Nie rozumiem, dlaczego zmienna sterująca musi pozostać w zakresie po wyjściu pętli. Z mojego doświadczenia wynika, że ​​po prostu zaśmieca to globalną przestrzeń nazw i utrudnia wyśledzenie błędów, które zostałyby wyłapane przez tłumaczy w innych językach.

chimeracoder
źródło
6
Jeśli nie chcesz, aby forpętla zaśmiecała Twoją globalną przestrzeń nazw, zawiń ją w funkcję. Mnóstwo zamknięć!
jathanizm
24
O ile nie uruchamiasz pętli w globalnej przestrzeni nazw (rzadkie), zaśmieca ona lokalną przestrzeń nazw.
Glenn Maynard
3
Gdyby to nie istniało, jak kontynuowałbyś przetwarzanie później, w punkcie, w którym przerwałeś w pętli? Po prostu zdefiniuj zmienną sterującą przed pętlą?
endolit
9
@endolith Yeah ... Dlaczego tego nie wymagać?
Steven Lu
3
Cóż, ludzie będą woleli to, do czego przywykli. Powiedziałbym, że tego rodzaju rzeczy ranią programistę Pythona, który przyzwyczaja się do tego typu rzeczy i musi przejść przez bolesny proces podczas przełączania się na inny język. Dla reszty z nas to chyba zgrabny skrót.
Steven Lu

Odpowiedzi:

107

Najbardziej prawdopodobną odpowiedzią jest to, że po prostu utrzymuje prostą gramatykę, nie jest przeszkodą do przyjęcia, a wielu z nich jest zadowolonych z braku konieczności ujednoznaczniania zakresu, do którego należy nazwa podczas przypisywania jej w konstrukcji pętli. Zmienne nie są zadeklarowane w zakresie, wynika to z położenia instrukcji przypisania. globalKluczowe istnieje tylko z tego powodu (aby zaznaczyć, że zadanie jest wykonywane w zakresie globalnym).

Aktualizacja

Oto dobra dyskusja na ten temat: http://mail.python.org/pipermail/python-ideas/2008-October/002109.html

Poprzednie propozycje uczynienia zmiennych pętli for lokalnymi w pętli natrafiły na problem istniejącego kodu, który opiera się na zachowaniu wartości zmiennej pętli po wyjściu z pętli i wydaje się, że jest to pożądane.

Krótko mówiąc, prawdopodobnie możesz winić za to społeczność Pythona: P

Jeremy Brown
źródło
2
Jak gramatyka byłaby bardziej skomplikowana, gdyby zakres zmiennej indukcyjnej był ograniczony do treści pętli? Taka zmiana ograniczałaby się do analizy semantycznej w Pythonie, a nie do gramatyki.
Charles
6
W Pythonie pętle nie są blokami. Ten rodzaj zmiany zachowania wymagałby albo fundamentalnej zmiany gramatyki, albo podania specjalnego przypadku. Cała koncepcja zmiennej indukcyjnej również nie jest wyrażona w aktualnej gramatyce. Gramatyka zawiera umowę dotyczącą tego, jak tłumacz będzie interpretował. Chodzi mi o to, że nie mogę przewidzieć, jak można zmienić to zachowanie bez komplikowania gramatyki. Wszystko to jest dyskusyjne, ponieważ efekt uboczny decyzji projektowej stał się cechą charakterystyczną.
Jeremy Brown
1
Ten post mail.python.org/pipermail/python-dev/2005-September/056677.html zawiera więcej szczegółów dotyczących szybkości i komplikacji, do których nawiązuje pan Brown.
rajesh
62

Python nie ma bloków, podobnie jak niektóre inne języki (takie jak C / C ++ lub Java). Dlatego jednostka określająca zakres w Pythonie jest funkcją.

atzz
źródło
3
Jestem zdezorientowany - co uniemożliwia Pythonowi określanie zakresu dla pętli w taki sam sposób, jak zakres funkcji?
chimeracoder
36
Nie do końca prawda, po prostu gramatyka nie oszaleje. ( docs.python.org/reference/… ) "Blok to fragment tekstu programu w języku Python, który jest wykonywany jako jednostka. Poniżej znajdują się bloki: moduł, treść funkcji i definicja klasy ..."
Jeremy Brown
1
@ od tyłu, nic. Po prostu uznano to za niepotrzebne.
habnabit
@Jeremy Brown - rzeczywiście. Dobra notatka.
atzz
6
@thebackhand - w językach z blokami forpętle zakresu są naturalnym rozszerzeniem ogólnej zasady. W Pythonie musiałby to być przypadek specjalny i należy unikać przypadków specjalnych, chyba że mają one istotne zalety.
atzz
39

Naprawdę przydatnym przypadkiem jest to, gdy używasz enumeratei chcesz na końcu uzyskać całkowitą liczbę:

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

Czy to konieczne? Nie. Ale z pewnością jest to wygodne.

Kolejna rzecz, o której należy pamiętać: w Pythonie 2 wyciekają również zmienne w listach składanych:

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

Ale to samo nie dotyczy Pythona 3.

Carl
źródło
4
Mogłeś to zrobić przypuszczalnie w elseklauzuli, tj. else: print "I did something {0} times".format(count)- zanim zniknie lokalny zakres (który nie istnieje w Pythonie)
Nas Banov
3
Tylko drugi przykład nie działa w Pythonie 3, prawda? Pierwszy nadal to robi? Uwagi, dlaczego został usunięty z Pythona 3?
endolit
7
dla count, item in enumerate (a, start = 1): # domyślny indeks wynosi od zera
Tao Zhang
3
Pierwszy przykład, zamiast być dobrym przykładem użycia, wydaje się bardziej dowodem na to, że ta zasada określania zakresu jest niebezpieczna i nie należy na niej polegać. A jeśli someiteratorjest pusty?
maksymalnie
1
@Nas Chociaż elsew tym przypadku można by użyć klauzuli, ogólnie nie zadziała, ponieważ treść pętli mogłaby zostać breakprzedwcześnie.
jamesdlin,
2

Jeśli masz w pętli instrukcję break (i ​​chcesz później użyć wartości iteracji, na przykład w celu pobrania kopii zapasowej, zindeksowania czegoś lub nadania statusu), oszczędza ona jedną linię kodu i jedno przypisanie, więc jest wygoda.

Prochowiec
źródło
1

Jednym z głównych czynników wpływających na Python jest ABC , język opracowany w Holandii do nauczania koncepcji programowania dla początkujących. Twórca Pythona, Guido van Rossum, przez kilka lat pracował nad ABC w latach 80. Nie wiem prawie nic o ABC, ale ponieważ jest przeznaczone dla początkujących, przypuszczam, że musi mieć ograniczoną liczbę zakresów, podobnie jak wczesne BASIC-y.

kindall
źródło
-1

Na początek, gdyby zmienne były lokalne dla pętli, te pętle byłyby bezużyteczne w większości rzeczywistych programów.

W obecnej sytuacji:

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

plony 45. Teraz zastanów się, jak działa przypisanie w Pythonie. Gdyby zmienne pętli były ściśle lokalne:

# 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

daje 0, ponieważ totalwewnątrz pętli po przypisaniu nie jest ta sama zmienna, co totalpoza pętlą. Nie byłoby to optymalne ani oczekiwane zachowanie.

Kirk Strauser
źródło
5
Nie odpowiadając na pytanie. OP pytał o foo, a nie total (lub bar w ich przykładzie).
James Bradbury
6
@JamesBradbury totali foonadal miałby lokalne powiązania pętli w scenariuszu OP, a logika jest taka sama.
Kirk Strauser,
2
OP: „Rozumiem, dlaczego może być konieczne, aby„ bar ”był dostępny poza pętlą (w przeciwnym razie pętle miałyby bardzo ograniczoną funkcjonalność). Nie rozumiem, dlaczego konieczne jest, aby zmienna sterująca pozostała w zakresie po zakończeniu pętli. " (moje podkreślenie)
James Bradbury,
2
@JamesBradbury Możesz mieć rację, ale odpowiedziałem na to trzy lata temu i prawdopodobnie nie warto teraz debatować.
Kirk Strauser