Python трохи дивний тим, що він зберігає все у словнику для різних областей. Оригінали a, b, c знаходяться в самому верхньому просторі і так у цьому верхньому словнику. У функції є власний словник. Коли ви дістаєтесь до print(a)
та print(b)
тверджень, у словнику нічого немає під цим іменем, тому Python шукає список і знаходить їх у глобальному словнику.
Тепер ми переходимо до c+=1
, що, звичайно, рівнозначно c=c+1
. Коли Python сканує цей рядок, він говорить "ага, є змінна назва" c ", я вкладу її в свій локальний словник діапазону". Потім, коли він шукає значення для c для c в правій частині завдання, він знаходить свою локальну змінну під назвою c , яка ще не має значення, і таким чином видаляє помилку.
global c
Згадане вище твердження просто говорить аналізатору, що він використовує c
з глобальної сфери, і тому він не потребує нового.
Причина, за якою вона каже, що в рядку є проблема, полягає в тому, що вона ефективно шукає імена, перш ніж вона намагається генерувати код, і тому в деякому сенсі не думає, що це дійсно ще робить цей рядок. Я б заперечував, що це помилка в застосуванні, але, як правило, хороша практика просто навчитися не сприймати повідомлення компілятора надто серйозно.
Якщо це будь-яка затишок, я, мабуть, пробув день, копаючи та експериментуючи з цим самим питанням, перш ніж знайшов щось, про що писав Гвідо про словники, які пояснювали все.
Оновлення, дивіться коментарі:
Він не сканує код двічі, але він сканує код у дві фази, лексінг та синтаксичний аналіз.
Поміркуйте, як працює розбір цього рядка коду. Лексер читає вихідний текст і розбиває його на лексеми, "найменші компоненти" граматики. Тож коли він потрапляє на лінію
c+=1
це розбиває його на щось подібне
SYMBOL(c) OPERATOR(+=) DIGIT(1)
Парсер в кінцевому підсумку хоче зробити це в дереві розбору та виконати його, але оскільки це призначення, перш ніж це зробити, він шукає ім'я c у локальному словнику, не бачить його та вставляє його у словник, позначаючи це як неініціалізований. Повністю складеною мовою він просто зайде в таблицю символів і чекає розбору, але оскільки НЕ БУДЕ розкіш другого проходу, лексери роблять трохи додаткової роботи, щоб згодом полегшити життя. Тільки тоді він бачить ОПЕРАТОР, бачить, що правила говорять "якщо у вас є оператор + = ліва частина повинна бути ініціалізована" і каже "ого!"
Сенс у тому, що він ще не почав синтаксичного розбору рядка . Це все відбувається на зразок підготовки до фактичного розбору, тому лічильник рядків не перейшов до наступного рядка. Таким чином, коли він сигналізує про помилку, він все ще вважає його на попередньому рядку.
Як я кажу, ви можете стверджувати, що це помилка зручності використання, але насправді це досить поширена річ. Деякі компілятори чесніше ставляться до цього і кажуть "помилка в рядку XXX або навколо", але цей - ні.