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

В Python пространство имен - это словарь, который сопоставляет имена с объектами. Когда вы назначаете строку переменной или создаете новый объект, Python добавляет ее в словарь пространства имен. Когда вы позже обратитесь к этой переменной в своей программе, Python обратится к пространству имен, чтобы получить ее. Этот процесс поиска состоит из нескольких уровней. Порядок идет от локальных - ›включающих -› глобальных - ›встроенных. Чтобы лучше понять это, давайте рассмотрим следующий пример.

Каждый модуль имеет глобальное пространство имен, и каждая функция создает отдельное локальное пространство имен. В этом примере мы объявили «а» три раза. Когда мы вызываем inner_namespace, Python сначала ищет «a» в локальном пространстве имен. Если он не найден, он будет смотреть на следующий уровень, вложение. Если его там нет, он попытается выполнить глобальную попытку. Обратите внимание, как «y» определяется глобально, но не локально. Python попытался разрешить «y» в локальном и охватывающем пространстве имен, но в конце концов пришлось прибегнуть к глобальному.

Ждать! А как насчет печати? Откуда это? Эта функция среди нескольких других находится на самом высоком уровне, известном как встроенные. Когда интерпретатор Python запускается, он загружает файл builtins.py и делает его общедоступным. Вот некоторые из них, с которыми вы, вероятно, знакомы: [‘sorted’, ‘staticmethod’, ‘str’, ‘sum’, ‘super’, ‘tuple’, ‘type’, ‘vars’, ‘zip’]. Выполните следующий код, чтобы увидеть полный список.

import builtins
print(dir(builtins))

Python позволяет управлять областью видимости с помощью двух ключевых слов: глобального и нелокального. В этом примере мы сделали «a» глобальным, поэтому мы можем получить к нему доступ вне внутренней функции. В противном случае будет выдана ошибка, поскольку «a» не существует в глобальном пространстве имен. Другое ключевое слово, нелокальное, позволяет нам изменять пространство имен test_namespace (). Их полезно знать и понимать, но старайтесь не полагаться на них, если у вас нет веской причины их использовать.

def test_namespace():
    y = 'enclosing y'
    
    def inner_namespace():
        global a
        a = 'local a made global'
        
        nonlocal y
        y = 'nonlocal y'
            
    inner_namespace()
    print(y)
test_namespace()
print(a)
---------output----------
nonlocal y
local a made global

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

globals()
locals()

Вызов locals () полезен для понимания рекурсии. В следующем примере мы увидим, как рекурсивно вычислить факториал любого числа.

def recursive_factorial(fact: list) -> int:
    print(locals())
    
    if len(fact) == 1:
        print('Hit base case')
        return fact[0]
    
    factorial = fact[0] * recursive_factorial(fact[1:])
    print(locals())
    
    return factorial
factorial = [5, 4, 3, 2, 1]
recursive_factorial(factorial)
---------output----------
{'fact': [5, 4, 3, 2, 1]}
{'fact': [4, 3, 2, 1]}
{'fact': [3, 2, 1]}
{'fact': [2, 1]}
{'fact': [1]}
Hit base case
{'fact': [2, 1], 'factorial': 2}
{'fact': [3, 2, 1], 'factorial': 6}
{'fact': [4, 3, 2, 1], 'factorial': 24}
{'fact': [5, 4, 3, 2, 1], 'factorial': 120}
120

На каждом этапе меняется локальное пространство имен. Сначала мы передаем весь список и ясно видим его в dict пространства имен, возвращаемом locals (). Затем мы умножаем первый элемент списка на факториал оставшихся элементов, пока не достигнем базового случая. После достижения базового случая мы разворачиваем вызовы и вычисляем факториал каждого шага. Этот код реализует факториальную формулу: n! = N × (n − 1) !.

Теперь вы можете понять, почему пространства имен являются очень важной особенностью Python. На каждом уровне мы можем иметь отдельные назначения для одного и того же имени переменной. В конце концов, это заявлено как «отличная идея» в this.py.