Глобальная видимость ключевых слов в Python внутри функции - PullRequest
2 голосов
/ 28 октября 2019

Я проверял, как глобальное ключевое слово работает для проекта, когда я по ошибке запускаю CODE1, который работал так, как я не ожидал. Ключевое слово global вступает в силу в функции, даже если оно не находится в исполняемой части (т. Е. В if, если условие не выполняется).

Я искал вопросы о глобальном ключевом слове в python, но яне смог найти ответ на это. Я видел: Глобальное поведение ключевых слов Python , Глобальное ключевое слово в Python , Использование глобальных переменных в функции

Самое интересное, и я думаю,возможно, с этим нужно что-то делать, вот это (но я не уверен): глобальное ключевое слово Python

Ниже показаны три минимальных воспроизводимых примера кода, который я использовал:1015 *

CODE1 (с глобальным ключевым словом):

a = 0
def my_function():
    b=2
    if b==0:
        print("first if")
        global a
    if b==2:
        print("second if")
        a = 2
        print("func -> a", a)
        print("func -> b", b)

if __name__ == '__main__':
    my_function()
    print("main -> a", a)

Результат:

second if
func -> a 2
func -> b 2
main -> a 2

CODE2 (без глобального ключевого слова):

a = 0
def my_function():
    b=2
    if b==0:
        print("first if")
    if b==2:
        print("second if")
        a = 2
        print("func -> a", a)
        print("func -> b", b)

if __name__ == '__main__':
    my_function()
    print("main -> a", a)

Результат:

second if
func -> a 2
func -> b 2
main -> a 0

CODE3 (с глобальным ключевым словом, но с инвертированными операторами if):

a = 0
def my_function():
    b=2
    if b==2:
        print("second if")
        a = 2
        print("func -> a", a)
        print("func -> b", b)
    if b==0:
        print("first if")
        global a

if __name__ == '__main__':
    my_function()
    print("main -> a", a)

Результат:

  File "global_var_test.py", line 18
    global a
    ^
SyntaxError: name 'a' is used prior to global declaration

Как видно, if b==0:всегда False и if b==2: всегда True (печать подтверждает это). Я ожидаю, что CODE1 даст тот же результат, что и CODE2, так как global a не будет выполнен в первом примере, поэтому он будет таким же, как и пропуск его. Но это дает неутешительный результат, в котором глобальное ключевое слово в любом случае вступает в силу, а глобальная переменная a изменяется на значение 2. После этого я протестировал с CODE3, полагая, что глобальное ключевое слово будет видно во всей функции независимо от ее положения, итогда CODE3 должен дать тот же результат, что и CODE1. Опять же, я был неправ, это сработало так, как если бы global a собирался быть исполненным (и тогда это было после присвоения и возбуждения исключения).

Затем мой последний вопрос: ¿делаетглобальное ключевое слово (и, возможно, другие, такие как нелокальные и т. д.) имеют видимость в коде в том порядке, в котором они написаны, но независимо от того, что выполняется?

Пожалуйста, помогите мнев разъяснении этого.

1 Ответ

3 голосов
/ 28 октября 2019

Мой ответ на этот вопрос может помочь понять некоторые технические детали здесь, хотя это немного другой вопрос.

Короче говоря, как вы обнаружили, компилятор Python будет в основномопределить область видимости переменной на основе того, как она впервые увидит, как она используется внутри функции;это не зависит от деталей, таких как управляющие операторы, поэтому, если случится встретить присваивание, подобное a = 2, перед тем как увидеть оператор global, он решит, что a является локальной переменной. Если вы попытаетесь инвертировать код (вы не привели пример, подобный этому), чтобы компилятор увидел оператор global first , он будет работать (хотя все еще будет плохим кодом):

a = 0
def my_function():
    b=2
    if b==2:
        print("second if")
        global a
        print("func -> a", a)
        print("func -> b", b)
    if b==0:
        print("first if")
        a = 2

Таким образом, для практических / технических, а также стилистических целей вы всегда должны объявлять global (или nonlocal) переменных в начале функции, а не где-либо еще.

Я не уверен, является ли это языковым требованием или деталью CPython;это был бы интересный дополнительный вопрос.

Обновление: Да, это требование спецификации языка;см. https://docs.python.org/3/reference/simple_stmts.html#grammar-token-global-stmt

Имена, перечисленные в операторе global, не должны использоваться в том же блоке кода , текстуально предшествующем этой глобальной инструкции.

Здесь текстуально предшествующий просто означает в терминах текста кода, независимо от окружающих деталей, таких как операторы управления. Это потому, что global на самом деле является директивой для синтаксического анализатора , который определяет, имеет ли переменная локальную или глобальную привязку, основываясь на том, как она впервые видит эту переменную. Хотя с точки зрения деталей реализации, это все еще может быть не совсем точным;например, CPython создает таблицу символов для модуля кода в качестве отдельного прохода через AST, возвращаемый анализатором. Таким образом, текстовый порядок кода также будет влиять на порядок прохождения узлов в AST. Например, вы можете увидеть, откуда пришло ваше сообщение об ошибке в посетителе для global операторов .

...