Нелокальный Python зависит от уровня иерархии? - PullRequest
9 голосов
/ 08 ноября 2011

Этот вопрос является продолжением вопроса о области видимости переменной Python . Дополнительные вопросы q1 , q2 и ответы можно найти на SO, и даже больше. Официальная документация Python и PEP 3104 должны объяснять детали, но они не кажутся мне полностью понятными.

Тема, которую я пытаюсь решить, - это рефакторинг кода, содержащего nonlocal / global, путем перемещения этого кода вверх / вниз на один уровень иерархии.

Что я не понимаю, так это значение этого предложения из ссылки на Python:

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

Учитывая следующий код в глобальной области видимости:

var = 0
def outer():
    global var                  # line A
    var = 1
    def inner():
        nonlocal var            # line B
        var = 2

        print('inner:', var)

    inner()
    print('outer:', var)

outer()
print('main:', var)

При выполнении возникает ошибка:

SyntaxError: no binding for nonlocal 'var' found

Код работает (конечно, с другой семантикой, если любая строка A закомментирована:

inner: 2
outer: 2
main: 0

или строка B закомментирована:

inner: 2
outer: 1
main: 1

Однако в вышеприведенном примере, и поскольку nonlocal должен связывать var с "включающей областью действия", я ожидал, что строка A связывает внешний / var с глобальной областью действия, а строка B затем ищет внешний / var, а также связывает внутренний / var с глобальным / var. Вместо этого он, кажется, вообще не находит его (из-за повторного связывания в строке A) и выдает ошибку.

Желаемый результат, который я ожидал, был:

inner: 2
outer: 2
main: 2

Это только еще один пример запутанного состояния ума в области видимости в Python?

Или, чтобы сделать это конструктивным вопросом:

  • Как можно написать такой пример таким образом, чтобы не имело значения, на каком уровне находится функция (необходимость обмена global с nonlocal и наоборот)?
  • Если функции находятся на промежуточном и неизвестном уровне иерархии, как мог автор outer() изменить код, который не должен касаться ни самого внешнего (в данном случае глобального) уровня, ни уровня inner() ? -

В моем скромном понимании языка подобных конструкций (зависимостей от замыканий) просто следует избегать. Другие уже предложили использовать другие языковые функции ( классы , func attrs ) для достижения такого рода чувствительности к контексту.

Ответы [ 2 ]

11 голосов
/ 09 ноября 2011

global и nonlocal не предназначены для комбинирования.Они означают разные вещи:

  • global означает, что имя существует на уровне модуля
  • nonlocal означает, что имя существует во внешней области лексической функции

Причина, по которой вы получаете исходное исключение, заключается в том, что вы сказали Python, что var является нелокальным (имеется в виду определение внешней функции), но нет привязки уровня функции для var в любом определении внешней функции, потому что вы сказали Python во внешней функции, что var является глобальным.

0 голосов
/ 09 ноября 2011

Как можно написать такой пример таким образом, чтобы не имело значения, на каком уровне находится функция (приходится обмениваться глобальным с нелокальным и наоборот)?

Это делаетне имеет значения, на каком уровне находится функция.Имеет значение только то, на каком уровне находится переменная.

Если функции находятся на промежуточном и неизвестном уровне иерархии, как мог автор external () изменить код, который не является самым внешним (вв этом случае глобальный) уровень, ни внутренний () уровень не должны быть затронуты?

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...