Рубрики
Без рубрики

Как использовать глобальные и нелокальные переменные в Python

В этом уроке мы рассмотрим, как использовать глобальные и нелокальные переменные в функциях Python. Мы расскажем о хороших практиках и о том, на что следует обратить внимание, приведя примеры.

Автор оригинала: Guest Contributor.

Вступление

В этой статье мы рассмотрим Глобальные и нелокальные переменные в Python и то, как их использовать, чтобы избежать проблем при написании кода.

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

Области применения в Python

Прежде чем мы сможем начать, мы сначала должны коснуться областей. Для тех из вас, кто менее знаком, “область действия” относится к контексту, в котором определена переменная и как к ней можно получить доступ или изменить или, более конкретно, – откуда к ней можно получить доступ.

И в программировании, как и в жизни, важен контекст.

Ссылаясь на Python прямо сейчас, вы можете сделать вывод из контекста, что я имею в виду язык программирования . Однако в другом контексте Python может быть ссылкой на змею или комедийную группу.

Глобальные и локальные области-это то, как ваша программа понимает контекст переменной, на которую вы ссылаетесь.

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

Локальные переменные в Python

Теперь, когда это понятно, давайте посмотрим на это в действии. Начнем с определения функции с ее собственной локальной переменной внутри. В этой функции у нас есть переменная fruit , которую мы инициализируем как список и печатаем:

def shopping_list():
    fruit = ['apple', 'banana']
    print(fruit)
    
shopping_list()

И, как и ожидалось, это работает как заклинание:

['apple', 'banana']

Но что происходит, когда мы перемещаем оператор print за пределы функции?

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

В частности, a 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 теперь является строкой, которая будет повторяться. Хуже всего то, что эта ошибка не проявит себя, пока, по-видимому, не станет слишком поздно. Первый код работал, казалось бы, нормально.

Теперь эта проблема нарочно очевидна. Мы непосредственно вмешались в глобальную переменную – о чудо, она изменилась. Однако в более сложных структурах можно случайно зайти слишком далеко в модификации глобальных переменных и получить неожиданные результаты.

Нелокальное ключевое слово

Просто потому, что вам нужно быть осторожным, не означает, что глобальные переменные также не невероятно полезны. Глобальные переменные могут быть полезны всякий раз, когда вы хотите обновить переменную, не предоставляя ее в операторе return, например счетчик. Они также очень удобны с вложенными функциями.

Для тех из вас , кто использует Python 3+ , вы можете использовать nonlocal , ключевое слово , которое функционирует очень похоже на global , но в основном вступает в силу, когда вложено в методы. нелокальный по существу образует промежуточный глобальный и локальный объем.

Поскольку мы использовали списки покупок и фрукты для большинства наших примеров, мы могли бы придумать функцию проверки, которая суммирует общую сумму покупок:

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 вне метода shopping bill:

NameError: name 'pct_off' is not defined

Если бы мы использовали ключевое слово global вместо ключевого слова nonlocal , печать pct_off привела бы к:

0.5

Вывод

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

Как всегда, большое вам спасибо за чтение и счастливый взлом!