Nonlocal python что это
Область видимости или scope определяет контекст переменной, в рамках которого ее можно использовать. В Python есть два типа контекста: глобальный и локальный.
Глобальный контекст
Глобальный контекст подразумевает, что переменная является глобальной, она определена вне любой из функций и доступна любой функции в программе. Например:
name = "Tom" def say_hi(): print("Hello", name) def say_bye(): print("Good bye", name) say_hi() say_bye()
Здесь переменная name является глобальной и имеет глобальную область видимости. И обе определенные здесь функции могут свободно ее использовать.
Локальный контекст
В отличие от глобальных переменных локальная переменная определяется внутри функции и доступна только из этой функции, то есть имеет локальную область видимости:
def say_hi(): name = "Sam" surname = "Johnson" print("Hello", name, surname) def say_bye(): name = "Tom" print("Good bye", name) say_hi() say_bye()
В данном случае в каждой из двух функций определяется локальная переменная name. И хотя эти переменные называются одинаково, но тем не менее это две разных переменных, каждая из которых доступна только в рамках своей функции. Также в функции say_hi() определена переменная surname, которая также является локальной, поэтому в функции say_bye() мы ее использовать не сможем.
Скрытие переменных
Есть еще один вариант определения переменной, когда локальная переменная скрывают глобальную с тем же именем:
name = "Tom" def say_hi(): name = "Bob" # скрываем значение глобальной переменной print("Hello", name) def say_bye(): print("Good bye", name) say_hi() # Hello Bob say_bye() # Good bye Tom
Здесь определена глобальная переменная name. Однако в функции say_hi определена локальная переменная с тем же именем name. И если функция say_bye использует глобальную переменную, то функция say_hi использует локальную переменную, которая скрывает глобальную.
Если же мы хотим изменить в локальной функции глобальную переменную, а не определить локальную, то необходимо использовать ключевое слово global :
name = "Tom" def say_hi(): global name name = "Bob" # изменяем значение глобальной переменной print("Hello", name) def say_bye(): print("Good bye", name) say_hi() # Hello Bob say_bye() # Good bye Bob
nonlocal
Выражение nonlocal прикрепляет идентификатор к переменной из ближайщего окружающего контекста (за исключением глобального контекста). Обычно nonlocal применяется во вложенных функциях, когда надо прикрепить идентификатор за переменной или параметром окружающей внешней функции. Рассмотрим ситуацию, где это выражение может пригодиться:
def outer(): # внешняя функция n = 5 def inner(): # вложенная функция print(n) inner() # 5 print(n) outer() # 5
Здесь вложенная локальная функция inner() выводит на консоль значение переменной n , которая определена во внешней функции outer(). Затем в функции outer() вызывается внутренняя функция inner().
При вызове функции outer() здесь мы ожидаемо увидим на консоли два раза число 5. Однако в данном случае вложенная функция inner() просто получает значение. Теперь возьмем другую ситуацию, когда вложенная функция присваивает значение переменной:
def outer(): # внешняя функция n = 5 def inner(): # вложенная функция n = 25 print(n) inner() # 25 print(n) outer() # 5 # 25 - inner # 5 - outer
При присвоении значения во вложенной функции: n = 25 будет создаваться новая переменная n, которая скроет переменную n из окружающей внешней функции outer. В итоге мы получим при выводе два разных числа. Чтобы во вложенной функции указать, что идентификатор во вложенной функции будет представлять переменную из окружающей функции, применяется выражение nonlocal :
def outer(): # внешняя функция n = 5 def inner(): # вложенная функция nonlocal n # указываем, что n - это переменная из окружающей функции n = 25 print(n) inner() # 25 print(n) outer() # 25
Как использовать global и nonlocal переменные в Python
В этой статье мы рассмотрим глобальные и нелокальные переменные в Python и как их использовать, чтобы избежать проблем при написании кода.
Мы начнем с краткого руководства по областям видимости переменных, прежде чем мы расскажем, как и почему использовать глобальные и нелокальные переменные в ваших собственных функциях.
Области видимости в Python
Прежде чем мы сможем начать, мы сначала должны коснуться областей. Для тех из вас, кто не знаком, «область видимости» относится к контексту, в котором определяется переменная и как к ней можно получить доступ или изменить или, более конкретно, откуда она может быть получена.
И в программировании, как и в жизни, важен контекст.
Ссылаясь на Python прямо сейчас, вы можете сделать вывод из контекста, что я имею в виду язык программирования. Однако в другом контексте Python может быть ссылкой на змею или комедийную группу.
Глобальная и локальная области видимости — это то, как ваша программа понимает контекст переменной, на которую вы ссылаетесь.
Как правило, переменные, определенные в функции или классе (как переменная экземпляра), по умолчанию являются локальными, а переменные вне функций и классов по умолчанию являются глобальными.
Локальные переменные в Python
Поняв это, давайте посмотрим на это в действии. Мы начнем с определения функции с ее собственной локальной переменной внутри. В этой функции у нас есть переменная fruit , которую мы инициализируем как список и печатаем:
def shopping_list(): fruit = ['apple', 'banana'] print(fruit) shopping_list()
Как и ожидалось, этот код выведет нам:
['apple', 'banana']
Но что происходит, когда мы перемещаем оператор печати за пределы функции?
def shopping_list(): fruit = ['apple', 'banana'] shopping_list() print(fruit)
Мы получаем ошибку:
Traceback (most recent call last): File "", line 5, in NameError: name 'fruit' is not defined
В частности, NameError , поскольку fruit был определен локально и поэтому остается ограниченным в этом контексте.
Чтобы наша программа могла понимать переменную глобально (вне функции), нам нужно определить ее глобально.
Глобальные переменные в Python
Что, если вместо первоначального определения нашей переменной внутри функции мы переместим ее наружу и инициализируем там?
В этом случае мы можем ссылаться на нее вне функции, и все работает.
Но если мы попытаемся переопределить переменную fruit внутри shopping_list , эти изменения не будут обновлены до исходной глобальной переменной, а будут изолированы локально:
fruit = ['apple', 'banana'] def shopping_list(): fruit = ['apple', 'banana', 'grapes'] shopping_list() print(fruit)
['apple', 'banana']
Это потому, что fruit мы изменили в функции shopping_list() создав новую локальную переменную. Мы создали ее, присвоили ей значение и после этого ничего не сделали. Это фактически полностью избыточный код. print() выводит значение глобальной переменной.
Ключевое слово global
Если мы хотим, чтобы эти изменения отражались в нашей глобальной переменной, вместо того, чтобы создавать новую локальную, все, что нам нужно сделать, это добавить ключевое слово global . Это позволяет нам сообщить, что переменная fruit действительно является глобальной переменной:
fruit = ['pineapple', 'grapes'] def shopping_list(): global fruit fruit = ['pineapple', 'grapes', 'apple', 'banana'] shopping_list() print(fruit)
И, конечно же, глобальная переменная модифицируется новыми значениями, поэтому, когда мы вызываем print(fruit) , новые значения печатаются:
['pineapple', 'grapes', 'apple', 'banana']
Определив контекст переменной fruit, которую мы называем глобальной, мы можем затем переопределить и изменить его, насколько нам нравится, зная, что изменения, которые мы вносим в функцию, будут перенесены.
Мы также могли бы определить глобальную переменную в нашей функции и иметь возможность ссылаться на нее и получать к ней доступ в любом другом месте.
def shopping_list(): global fruit fruit = ['pineapple', 'grapes', 'apple', 'banana'] shopping_list() print(fruit)
['pineapple', 'grapes', 'apple', 'banana']
Мы могли бы даже объявить глобальную переменную в одной функции и получить к ней доступ в другой, не определяя ее как глобальную во второй:
def shopping_list(): global fruit fruit = ['pineapple', 'grapes', 'apple', 'banana'] def print_list(): print(fruit) shopping_list() print(fruit) print_list()
['pineapple', 'grapes', 'apple', 'banana'] ['pineapple', 'grapes', 'apple', 'banana']
Осторожность при использовании глобальных переменных
Хотя возможность локально изменять глобальную переменную — это небольшой удобный инструмент, к нему нужно относиться с некоторой осторожностью. Чрезмерное переписывание и переопределение области видимости — это рецепт катастрофы, которая заканчивается ошибками и неожиданным поведением.
Всегда важно убедиться, что вы манипулируете переменной только в том контексте, который вам нужен, а в противном случае, оставив его в покое, это основной движущий принцип принципа инкапсуляции.
Мы быстро рассмотрим пример потенциальной проблемы, прежде чем перейти к некоторым из способов, которыми глобальные переменные могут быть полезны в вашем собственном коде:
fruit = ['pineapple', 'grapes', 'apple', 'banana'] def first_item(): global fruit fruit = fruit[0] def iterate(): global fruit for entry in fruit: print(entry) iterate() print(fruit) first_item() print(fruit)
Запустив приведенный выше код, мы получим следующий вывод:
pineapple grapes apple banana ['pineapple', 'grapes', 'apple', 'banana'] pineapple
В этом примере мы ссылаемся на переменную в обеих функциях first_item() и iterate() . Все вроде работает нормально, если вызвать iterate() и потом first_item() .
Если мы изменим этот порядок или попытаемся повторить его позже, мы столкнемся с большой проблемой:
first_item() print(fruit) iterate() print(fruit)
Теперь это выводит:
pineapple p i n e a p p l e pineapple
А именно, теперь fruit это строка, которая будет повторяться. Что еще хуже, эта ошибка не проявляется, пока, по-видимому, не станет слишком поздно. Первый код вроде бы работал нормально.
Теперь эта проблема очевидна намеренно. Мы напрямую изменили глобальную переменную — и вот, она изменилась. Однако в более сложных структурах можно случайно зайти слишком далеко от модификации глобальной переменной и получить неожиданные результаты.
Ключевое слово nonlocal
То, что вам нужно быть осторожным, не означает, что глобальные переменные также не очень полезны. Глобальные переменные могут быть полезны всякий раз, когда вы хотите обновить переменную, не указывая ее в операторе возврата, например счетчик. Они также очень удобны с вложенными функциями.
Для тех из вас, кто использует Python 3+ , вы можете использовать ключевое слово nonlocal , которое действует аналогично global , но в основном действует при вложении в методы. nonlocal по сути образует промежуточное звено между глобальной и локальной областью.
Поскольку в большинстве наших примеров мы использовали списки покупок и фрукты, мы могли бы подумать о функции оформления заказа, которая суммирует сумму покупок:
def shopping_bill(promo=False): items_prices = [10, 5, 20, 2, 8] pct_off = 0 def half_off(): nonlocal pct_off pct_off = .50 if promo: half_off() total = sum(items_prices) - (sum(items_prices) * pct_off) print(total) shopping_bill(True)
Запустив приведенный выше код, мы получим результат:
Таким образом, глобальная переменная count по-прежнему является локальной для внешней функции и не влияет (или не существует) на более высоком уровне. Это дает вам некоторую свободу в добавлении модификаторов к вашим функциям.
Вы всегда можете подтвердить это, попробовав распечатать pct_off не с помощью метода подсчета покупок:
NameError: name 'pct_off' is not defined
Если бы мы использовали ключевое слово global вместо nonlocal , печать привела бы к pct_off :
Заключение
В конце концов, global (nonlocal) ключевые слова — это инструмент, и при правильном использовании они могут открыть множество возможностей для вашего кода. Лично я довольно часто использую оба этих ключевых слова в своем собственном коде, и при достаточной практике вы поймете, насколько мощными и полезными они могут быть.
Источник:
global и nonlocal
Внутри функций Python мы можем использовать значения глобальных переменных, т.е. определенных вне любых функций, на уровне модуля:
def foo(): print('x is', x) x = 5 foo() # напечает x is 5
Однако если в функции есть присваиваниие x после использования переменной x, то возникнет ошибка:
def foo(): print('x is', x) x = 10 x = 5 foo() # UnboundLocalError: local variable 'x' referenced before assignment
Обатите внимание, что присваивание бывает в следующих ситуациях:
• x = …
• x += …, x -= … и т.п.
• for x in … :
• with … as x :
Чтобы избежать ошибки, мы должны явно указать перед использованием x, что она относится к глобальной области видимости:
def foo(): global x #Подобная проблема возникает и для вложенных функций, когда во внутренней функции мы хотим поймать в замыкание переменную из внушней функции, чтобы далее присвоить ей другое значение. Вот пример – функция, создающее увеличивающийся на 1 счечтик:
def make_inc(): # внешняя ф-ция total = 0 # счетчик def helper(): # внутр. ф-ция total += 1 # тут присваивание переменной return total return helper f = make_inc() print(f()) # UnboundLocalError: local variable 'total' referenced before assignmentТут нужно другое ключевое слово – nonlocal, которое укажет, что нужно искать переменную во внешней области видимости. Такой пример будет работать, как задумано:
def make_inc(): total = 0 def helper(): nonlocal total #Почему мы редко видим global и nonlocal ?
nonlocal – специфичная вещь, обычно вместо нее создают класс.
global потакает плохим практикам программирования. Менять глобальные переменные внутри функций – плохая практика.def foo(): global x print('x is', x) for x in range(2): . x = 5 foo() # x is 5 foo() # x is 1 (испортили из-за for)Нет ошибок выполнения, но есть логическая ошибка! После первого вызова foo() мы испортили глобальную переменную x , она стала 1 (последним значением в цикле). Надо было просто называть переменные разными именами, и global не понадобится!
Специально для канала @pyway. Подписывайтесь за новыми статьями. Будет интересно!
Инструкции global и nonlocal в Python, как использовать
Ссылки для тех кто ищет встроенные функции globals() и locals() .
Инструкция global :
Инструкция global это объявление, которое выполняется для всего текущего блока кода. Это означает, что перечисленные в инструкции переменные должны интерпретироваться как глобальные. Свободные переменные могут ссылаться на глобальные переменные с помощью global , не будучи объявленными в глобальной области.
Имена, перечисленные в инструкции global , не должны использоваться в блоке кода, текстуально предшествующем global .
Имена, перечисленные в инструкции global , не должны быть определены как аргументы функции или использоваться в целевом элементе item управления циклом for item in . . Не должны быть связаны с именем в определении класса, определении функции, оператора import или аннотации переменной.
Инструкция global , переданная в строке или объекте кода в функцию exec() , не влияет на блок кода, содержащий вызов функции, и на код, содержащийся в такой строке. То же самое относится к функциям eval() и compile() .
Посмотреть все глобальные переменные можно при помощи вызова встроенной функции globals() из любой области видимости.
Инструкция nonlocal :
Инструкция nonlocal вызывает перечисленные имена для обозначения ранее определенных переменных в ближайшей области видимости, исключая глобальную. Это важно потому, что интерпретатор сначала выполняет поиск в локальном пространстве имен. Оператор позволяет инкапсулированному коду связывать переменные вне локальной области, кроме глобальной (модульной) области.
Имена, перечисленные в инструкции nonlocal , в отличие от тех, что перечислены в инструкции global , должны ссылаться на ранее существовавшие переменные в охватывающей области.
Имена, перечисленные в выражении nonlocal , не должны вступать в противоречие с ранее существующими переменными, определенными локальной области видимости.
Посмотреть все локальные переменные определенной области видимости можно при помощи вызова встроенной функции locals() из этой области видимости.
Обобщая сказанное:
- операторы global и nonlocal используются в области видимости той функции, где будет требуется изменение переменной;
- оператор global можно использовать как в простых функциях, так и во вложенных;
- оператор nonlocal используются во вложенных функциях с переменными, которые уже существуют в ближайшей области видимости;
- с помощью оператора global можно определить глобальную переменную из области видимости функции, которая ранее отсутствовала;
- при использовании оператора nonlocal , переменные родительской функции должны существовать.
Важно:
Никогда не используйте инструкцию global , если глобальная переменная только используется в теле функции. Применяйте эту инструкцию, когда функция изменяет связывание глобальной переменной. Хороший стиль программирования предполагает использование инструкции global исключительно в случае, когда это остро необходимо, так как ее наличие в коде заставляет предполагать, что она включена в программу для каких то полезных целей. Если, все таки, без нее невозможно обойтись, то пишите оператор global в блоке функции в качестве первой инструкции.
Примеры использования global и nonlocal
Изменение глобальной переменной внутри функции с помощью global .
def plus(): print('В коде определены глобальные переменные:\n', key: val for key, val in globals().items() if 'global' in key>) # Локальная область видимости функции plus() # Здесь глобальные переменные ДОСТУПНЫ ТОЛЬКО ДЛЯ ЧТЕНИЯ print('=> Попытка изменить глобальную переменную `global_a`') try: global_a = global_a + 1 except UnboundLocalError: print('. Ошибка: UnboundLocalError (Локальное имя указано, но не привязано к значению)\n' 'Эта ошибка говорит о том, что глобальную переменную `global_a` ' 'НЕЛЬЗЯ ИЗМЕНИТЬ внутри функции plus()') finally: global_a = 1 # Здесь создалась новая локальная переменная функции plus(), # которая хранит свое значение только внутри функции plus() # и не имеет ни чего общего с глобальной переменной `global_a`. # В Python это называется скрытием глобальной переменной print('global_a =', global_a, '=> создана новая локальная переменная ' 'функции plus()') # Укажем, что `global_b` - глобальная, для функции plus() global global_b print('Доступные переменные в функции `plus()`:', locals()) # Изменим глобальную переменную `global_b` на 100 global_b = global_b + 50 # Глобальная область видимости # Определим глобальные переменные global_a = 10 global_b = 100 # вызываем функцию plus() print(global_a, '=> Глобальная переменная `global_a` НЕ изменилась.') print(global_b, '=> Глобальная переменная `global_b` изменена ' 'внутри функции plus()')
Создание глобальной переменной из локальной области видимости функции.
def plus(): # Локальная область видимости функции `plus()` print('Словарь глобальных переменных в начале определения функции `plus()`:\n', key: val for key, val in globals().items() if 'global' in key>) # `global_a` еще ни где не определена global global_a # Глобальные переменные могут отсутствовать при их создании global_a = 50 # Создаст переменную `global_a` в глобальной области видимости global_a = global_a - 10 def nested(): """Вложенная функция `nested()`""" # локальная область видимости вложенной функции nested() # Что бы изменить глобальную переменную `global_a`, мы # должны указать с помощью оператора global, что эта # переменная для вложенной функции то же является глобальной global global_a global_a = global_a - 10 # С помощью оператора `global` можно создавать # глобальные переменные, так же во вложенных функциях global global_b global_b = 0 # Создаст переменную `global_b` в глобальной области видимости global_b = global_b + 5 # Выполним вложенную функцию nested() # Изменим глобальную переменную `global_b`, созданную # во вложенной функции, для этого укажем с помощью # оператора `global`, что эта переменная для родительской # функции то же является глобальной global global_b global_b = global_b + 5 print('Словарь глобальных переменных в конце определения функции `plus()`:\n', key: val for key, val in globals().items() if 'global' in key>) # Глобальная область видимости. Какие либо переменные отсутствуют. # вызов функции plus() plus() global_a = global_a + 100 print(global_a, '=> глобальная переменная global_a, созданная ' 'в функции plus() изменена в глобальной области видимости') global_b = global_b + 50 print(global_b, '=> глобальная переменная global_b, созданная ' 'во вложенной функции nested() изменена в глобальной области видимости')
Изменение переменных в соседних областях видимости с помощью nonlocal .
def plus(): # Локальная область видимости функции `plus()` # Определим локальные переменные функции `plus()` plus_a = 5 plus_b = 10 print('Локальные переменные функции `plus()`:', locals()) def nested(): """Вложенная функция `nested()`""" # локальная область видимости вложенной функции `nested()` # Здесь локальные переменные функции `plus()` ДОСТУПНЫ ТОЛЬКО ДЛЯ ЧТЕНИЯ print('=> Попытка изменить локальную переменную функции `plus()`') try: plus_a = plus_a + 1 except UnboundLocalError: print('. Ошибка: UnboundLocalError (Локальное имя указано, но не привязано к значению)\n' 'Эта ошибка говорит о том, что локальную переменную `plus_a`, созданную во внешней ' 'функции `plus()` НЕЛЬЗЯ ИЗМЕНИТЬ внутри вложенной функции nested()') finally: plus_a = 1 # Здесь создалась новая локальная переменная функции `nested()`, # которая хранит свое значение только внутри функции `nested()` # и не имеет ни чего общего с локальной переменной plus_a, которая # определялась ранее в локальной области видимости функции `plus()` print('plus_a =', plus_a, '=> создана новая локальная переменная ' 'в области видимости функции `nested()`') # Укажем, что plus_b - не локальная, для вложенной функции `nested()` # . Нелокальные переменные должны существовать, т.е. должны быть # определены в ближайшей (вышестоящей) области видимости)! nonlocal plus_b print('Доступные переменные в функции `nested()`', locals()) plus_b = plus_b + 10 print(plus_b, '=> Переменная plus_b, локальной области функции plus(), ' 'изменена внутри области видимости вложенной функции `nested()`') # вызов вложенной функции `nested()` внутри функции `plus()` nested() print(plus_a, '=> Локальная переменная plus_a, созданная в функции ' '`plus()` НЕ изменилась.') print(plus_b, '=> Локальная переменная plus_b, созданная в функции ' '`plus()` изменена внутри вложенной функции nested()') ###################### # вызов функции plus() plus()
- КРАТКИЙ ОБЗОР МАТЕРИАЛА.
- Функции это объекты
- Функции могут иметь атрибуты
- Функции могут храниться в структурах данных
- Функции могут быть вложенными
- Передача функции в качестве аргумента другой функции
- Область видимости переменных функции
- Операторы global и nonlocal
- Параметры (аргументы) функции
- Ключевые аргументы в определении функции Python
- Значение аргумента по умолчанию в функциях Python
- Варианты передачи аргументов в функцию Python
- Переменные аргументов *args и **kwargs в функции Python
- Распаковка аргументов для передачи в функцию Python
- Как оцениваются аргументы при вызове функции?
- Строгие правила передачи аргументов в функцию Python
- Инструкция return
- Анонимные функции (lambda-выражения)
- Строки документации в функциях Python
- Рекурсия
- Замыкания в функциях Python
- Перегрузка функций
