Вступление
В этой статье мы рассмотрим глобальные и нелокальные переменные в 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 "<string>", line 5, in <module>
NameError: name 'fruit' is not defined
В частности, NameError
, поскольку плод был определен локально и
поэтому остается ограниченным этим контекстом.
Чтобы наша программа могла понимать переменную глобально (вне функции),
нам нужно определить ее глобально.
Глобальные переменные в 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
Это позволяет нам
общаться , что 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
теперь является строкой, которая будет повторяться.
Что еще хуже, эта ошибка не проявляется, пока, по-видимому, не станет
слишком поздно. Первый код вроде бы работал нормально.
Теперь эта проблема очевидна намеренно. Мы напрямую изменили глобальную переменную - о чудо, она изменилась. Однако в более сложных структурах можно случайно зайти слишком далеко от модификации глобальной переменной и получить неожиданные результаты.
Нелокальное ключевое слово
То, что вам нужно быть осторожным, не означает, что глобальные переменные также не очень полезны. Глобальные переменные могут быть полезны всякий раз, когда вы хотите обновить переменную, не указывая ее в операторе возврата, например счетчик. Они также очень удобны с вложенными функциями.
Те из вас, кто использует 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)
Запустив приведенный выше код, мы получаем результат:
22.5
Таким образом, глобальная переменная count остается локальной по отношению к внешней функции и не влияет (или не существует) на более высоком уровне. Это дает вам некоторую свободу в добавлении модификаторов к вашим функциям.
Вы всегда можете подтвердить это, попробовав напечатать pct_off
вне
метода счета покупок:
NameError: name 'pct_off' is not defined
Если бы мы использовали global
ключевое слово вместо nonlocal
ключевого слова, печати pct_off
приведет:
0.5
Заключение
В конце концов, глобальные (и нелокальные) ключевые слова - это инструмент, и при правильном использовании они могут открыть множество возможностей для вашего кода. Лично я довольно часто использую оба этих ключевых слова в своем собственном коде, и при достаточной практике вы поймете, насколько мощными и полезными они могут быть.
Как всегда, большое спасибо за чтение и удачного взлома!