Оцінка в Python 'для' циклів


177

Я не розпитую про правила розміщення Python; Я взагалі розумію, як працює масштабування в Python для циклів. Моє питання - чому дизайнерські рішення приймалися таким чином. Наприклад (каламбур не призначений):

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

Вище буде надруковано (9,2).

Це вражає мене як дивне: "foo" насправді просто керує циклом, а "bar" було визначено всередині циклу. Я можу зрозуміти, чому може знадобитися доступ до "бар" поза циклом (інакше для циклів буде дуже обмежена функціональність). Що я не розумію, це те, чому необхідно, щоб контрольна змінна залишалася в області застосування після завершення циклу. На мій досвід, він просто захаращує глобальний простір імен і ускладнює відстеження помилок, які траплялися б перекладачами інших мов.


6
Якщо ви не хочете, щоб forпетля захаращувала вашу глобальну область імен, вставте її у функцію. Закриття неохайні!
ятанізм

24
Якщо ви не запускаєте цикл у глобальному просторі імен (рідко), це захаращує локальний простір імен.
Гленн Мейнард

3
Якби цього не було, як би ви продовжували обробку пізніше в точці, яку ви залишили всередині циклу? Просто визначте змінну управління перед циклом?
ендоліт

9
@endolith Так ... Чому цього не вимагати?
Стівен Лу

3
ну люди просто віддають перевагу тому, що звикли робити. Я б сказав, що така штука шкодить кодеру пітона, який звикає до подібних речей і мусить пройти через болісний процес при переході на іншу мову. Для решти нас, я думаю, це акуратний невеликий ярлик.
Стівен Лу

Відповіді:


107

Найімовірніша відповідь полягає в тому, що вона просто зберігає граматику простою, не була каменем спотикання для прийняття, і багато хто із задоволенням не мав розмежувати сферу, до якої належить ім'я, присвоюючи йому всередині контуру конструкцію. Змінні не задекларовані в межах області, це мається на увазі за розташуванням операторів призначення. globalКлючове слово існує тільки з цієї причини (для позначення , що призначення робиться в глобальному масштабі).

Оновлення

Ось хороша дискусія на тему: http://mail.python.org/pipermail/python-ideas/2008-O жовтня/002109.html

Попередні пропозиції щодо створення змінних for-loop, локальних для циклу, натрапили на проблему існуючого коду, який покладається на те, що змінна циклу зберігає своє значення після виходу з циклу, і, здається, це вважається бажаною особливістю.

Коротше кажучи, ви можете, мабуть, звинуватити це у спільноті Python: P


2
Як би граматика була складнішою, якби область змінної індукції була обмежена тілом циклу? Така зміна обмежилася б семантичним аналізом в Python, а не його граматикою.
Чарльз

6
Петлі не є блоками в Python. Така зміна поведінки вимагатиме або кардинальної зміни граматики, або надання особливого випадку. Все поняття індукційної змінної також не виражається в поточній граматиці. Граматика надає договір про те, як інтерпретуватиметься перекладач. Моя думка полягає в тому, що я не можу передбачити, як можна змінити цю поведінку, не ускладнивши граматику. Це все суперечливо, оскільки побічний ефект дизайнерського рішення став особливістю.
Джеремі Браун

1
У цьому дописі mail.python.org/pipermail/python-dev/2005-September/056677.html наведено більше інформації про швидкість та ускладнення, на які натякає містер Браун.
rajesh

62

У Python немає блоків, як у деяких інших мовах (наприклад, C / C ++ або Java). Отже, одиниця масштабування в Python - це функція.


3
Мене бентежить - що заважає Python проводити обробку циклів так само, як і функції, на які поширюються функції?
химерний кодер

36
Насправді це не правда, це просто те, що граматика не збивається з розуму. ( docs.python.org/reference/… ) "Блок - це фрагмент тексту програми Python, який виконується як одиниця. Це блоки: модуль, тіло функції та визначення класу ..."
Джеремі Коричневий

1
@thebackhand, нічого. Це було просто визнано непотрібним.
ханабіт

@Jeremy Brown - справді. Гарна примітка.
atzz

6
@thebackhand - мовами з блоками скопірування forциклів - це природне продовження загального принципу. У Python це повинно бути особливим випадком, а особливих випадків слід уникати, якщо вони не матимуть вагомих переваг.
atzz

39

Дійсно корисний випадок для цього, коли enumerateви користуєтесь, і ви хочете, щоб сумарна кількість була врешті-решт:

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

Це потрібно? Ні. Але це, звичайно, зручно.

Ще одна річ, про яку слід пам’ятати: у Python 2 також просочуються змінні в розумінні списку:

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

Але те ж саме не стосується Python 3.


4
Ви могли це зробити, мабуть, у elseпункті, тобто. else: print "I did something {0} times".format(count)- до того, як локальний обсяг (якого не існує в Python) зникне
Нас Банов

3
Тільки другий приклад не працює в Python 3, правда? Перший все-таки робить? Примітки, чому його було видалено з Python 3?
ендоліт

7
для підрахунку, пункт у перерахунку (a, start = 1): # індекс за замовчуванням дорівнює нулю
Tao Zhang

3
Перший приклад, а не як корисний випадок використання, більше схожий на доказ того, що це правило масштабування небезпечне і на нього не слід покладатися. Що робити, якщо someiteratorпорожній?
макс

1
@Nas Хоча elseв цьому випадку може бути використаний пункт, він взагалі не працює, оскільки тіло циклу може breakпередчасно.
jamesdlin

2

Якщо у вас є цикл перерви в циклі (і ви хочете використовувати значення ітерації пізніше, можливо, щоб отримати резервне копіювання, індексувати щось або надати статус), це заощадить вам один рядок коду та одне призначення, тому є зручність.


1

Одним з основних впливів для Python є ABC - мова, розроблена в Нідерландах для навчання початківцям концепцій програмування. Творець Пітона, Гідо ван Россум, працював над ABC кілька років у 1980-х. Я майже нічого не знаю про ABC, але так як він призначений для початківців, я думаю, він повинен мати обмежену кількість областей, як і ранні BASIC.


-1

Для початківців, якби змінні були локальними для циклів, ці петлі були б марними для більшості програм реального життя.

У ситуації, що склалася:

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

врожайність 45. Тепер розглянемо, як працює призначення в Python. Якщо змінні циклу були строго локальними:

# 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

урожайність 0, тому що totalвсередині циклу після призначення не така змінна, як totalпоза циклом. Це не було б оптимальним чи очікуваним поведінкою.


5
Не відповідаючи на запитання. ОП запитували про foo, не загальний (або смугу в їх прикладі).
Джеймс Бредбері

6
@JamesBradbury totalі fooвсе-таки матимуть локальні зв'язки в сценарії ОП, і логіка така ж.
Кірк Штраузер

2
ОП: "Я можу зрозуміти, чому може бути необхідним, щоб" бар "був доступний за межами циклу (інакше для циклів буде дуже обмежена функціональність). Я не розумію, чому необхідно, щоб контрольна змінна залишалася в області після закінчення циклу. " (акцент мій)
Джеймс Бредбері

2
@JamesBradbury Ви можете мати рацію, але я відповів на це три роки тому, і зараз, мабуть, не варто дискутувати.
Кірк Штраузер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.