Статьи

Что такое пространства имен Python (и зачем они нужны?)

Конфликты имен происходят все время в реальной жизни. Например, в каждой школе, в которой я когда-либо учился, было по крайней мере два ученика в моем классе, которые имели одно и то же имя. Если кто-то пришел в класс и попросил ученика X, мы бы с энтузиазмом спросили: «О ком ты говоришь? Есть два ученика по имени X». После этого запрашивающий человек назовет нам фамилию, и мы представим его справа X.

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

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

Пространство имен — это, по сути, система, которая гарантирует, что все имена в программе уникальны и могут использоваться без каких-либо конфликтов. Возможно, вы уже знаете, что все в Python — например, строки, списки, функции и т. Д. — является объектом. Еще один интересный факт заключается в том, что Python реализует пространства имен как словари. Существует сопоставление имени с объектом, имена которого являются ключами, а объекты — значениями. Несколько пространств имен могут использовать одно и то же имя и сопоставлять его с другим объектом. Вот несколько примеров пространств имен:

  • Локальное пространство имен: это пространство имен включает локальные имена внутри функции. Это пространство имен создается при вызове функции и длится до тех пор, пока функция не вернется.
  • Глобальное пространство имен: это пространство имен включает имена из различных импортированных модулей, которые вы используете в проекте. Он создается, когда модуль включен в проект, и длится до конца скрипта.
  • Встроенное пространство имен. Это пространство имен включает встроенные функции и имена встроенных исключений.

В серии « Математические модули в Python » на Envato Tuts + я писал о полезных математических функциях, доступных в различных модулях. Например, модули math и cmath имеют много общих функций, таких как log10() , acos() , cos() , exp() и т. Д. Если вы используете оба этих модуля в В той же программе единственный способ однозначно использовать эти функции — это префиксировать их именем модуля, например math.log10() и cmath.log10() .

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

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

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

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

Прежде чем мы начнем, попробуйте ввести dir() в IDLE или любой другой Python IDE.

1
2
dir()
# [‘__builtins__’, ‘__doc__’, ‘__loader__’, ‘__name__’, ‘__package__’, ‘__spec__’]

Все эти имена, перечисленные в dir() , доступны в каждой программе Python. Для краткости я начну называть их '__builtins__'...'__spec__' в остальных примерах.

Давайте посмотрим на вывод функции dir() после определения переменной и функции.

01
02
03
04
05
06
07
08
09
10
11
12
13
a_num = 10
dir()
# [‘__builtins__’ …. ‘__spec__’, ‘a_num’]
 
def some_func():
    b_num = 11
    print(dir())
     
some_func()
# [‘b_num’]
 
dir()
# [‘__builtins__’ … ‘__spec__’, ‘a_num’, ‘some_func’]

Функция dir() выводит только список имен внутри текущей области. Вот почему внутри области some_func() есть только одно имя с именем b_num . Вызов dir() после определения some_func() добавляет его в список имен, доступных в глобальном пространстве имен.

Теперь давайте посмотрим список имен внутри некоторых вложенных функций. Код в этом блоке продолжается с предыдущего блока.

01
02
03
04
05
06
07
08
09
10
11
12
def outer_func():
    c_num = 12
    def inner_func():
        d_num = 13
        print(dir(), ‘ — names in inner_func’)
    e_num = 14
    inner_func()
    print(dir(), ‘ — names in outer_func’)
     
outer_func()
# [‘d_num’] — names in inner_func
# [‘c_num’, ‘e_num’, ‘inner_func’] — names in outer_func

Приведенный выше код определяет две переменные и функцию внутри области outer_func() . Внутри inner_func() функция dir() печатает только имя d_num . Это кажется справедливым, поскольку d_num — единственная переменная, определенная там.

Если явно не указано использование global , переназначение глобального имени в локальном пространстве имен создает новую локальную переменную с тем же именем. Это видно из следующего кода.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
a_num = 10
b_num = 11
 
def outer_func():
    global a_num
    a_num = 15
    b_num = 16
    def inner_func():
        global a_num
        a_num = 20
        b_num = 21
        print(‘a_num inside inner_func :’, a_num)
        print(‘b_num inside inner_func :’, b_num)
    inner_func()
    print(‘a_num inside outer_func :’, a_num)
    print(‘b_num inside outer_func :’, b_num)
     
outer_func()
print(‘a_num outside all functions :’, a_num)
print(‘b_num outside all functions :’, b_num)
 
# a_num inside inner_func : 20
# b_num inside inner_func : 21
 
# a_num inside outer_func : 20
# b_num inside outer_func : 16
 
# a_num outside all functions : 20
# b_num outside all functions : 11

Внутри outer_func() и inner_func() , что a_num является глобальной переменной. Мы просто устанавливаем другое значение для одной и той же глобальной переменной. По этой причине значение a_num во всех местоположениях равно 20. С другой стороны, каждая функция создает свою собственную переменную b_num с локальной областью действия, а функция print() печатает значение этой переменной локальной области видимости.

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

  • from module import * : Этот метод импорта модуля импортирует все имена из данного модуля непосредственно в ваше текущее пространство имен. Вы можете испытать желание использовать этот метод, потому что он позволяет вам использовать функцию напрямую, не добавляя имя модуля в качестве префикса. Однако это очень подвержено ошибкам, и вы также теряете возможность сказать, какой модуль фактически импортировал эту функцию. Вот пример использования этого метода:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
dir()
# [‘__builtins__’ … ‘__spec__’]
 
from math import *
dir()
# [‘__builtins__’ … ‘__spec__’, ‘acos’, ‘acosh’, ‘asin’, ‘asinh’,
# ‘atan’, ‘atan2’, ‘atanh’, ‘ceil’, ‘copysign’, ‘cos’, ‘cosh’, ‘degrees’,
# ‘e’, ‘erf’, ‘erfc’, ‘exp’, ‘expm1’, ‘fabs’, ‘factorial’, ‘floor’, ‘fmod’,
# ‘frexp’, ‘fsum’, ‘gamma’, ‘gcd’, ‘hypot’, ‘inf’, ‘isclose’, ‘isfinite’,
# ‘isinf’, ‘isnan’, ‘ldexp’, ‘lgamma’, ‘log’, ‘log10’, ‘log1p’, ‘log2’,
# ‘modf’, ‘nan’, ‘pi’, ‘pow’, ‘radians’, ‘sin’, ‘sinh’, ‘sqrt’, ‘tan’,
# ‘tanh’, ‘trunc’]
 
log10(125)
# 2.0969100130080562
 
from cmath import *
dir()
# [‘__builtins__’ … ‘__spec__’, ‘acos’, ‘acosh’, ‘asin’, ‘asinh’, ‘atan’,
# ‘atan2’, ‘atanh’, ‘ceil’, ‘copysign’, ‘cos’, ‘cosh’, ‘degrees’, ‘e’, ‘erf’,
# ‘erfc’, ‘exp’, ‘expm1’, ‘fabs’, ‘factorial’, ‘floor’, ‘fmod’, ‘frexp’, ‘fsum’,
# ‘gamma’, ‘gcd’, ‘hypot’, ‘inf’, ‘isclose’, ‘isfinite’, ‘isinf’, ‘isnan’,
# ‘ldexp’, ‘lgamma’, ‘log’, ‘log10’, ‘log1p’, ‘log2’, ‘modf’, ‘nan’, ‘phase’,
# ‘pi’, ‘polar’, ‘pow’, ‘radians’, ‘rect’, ‘sin’, ‘sinh’, ‘sqrt’, ‘tan’, ‘tanh’,
# ‘trunc’]
 
log10(125)
# (2.0969100130080562+0j)

Если вы знакомы с модулями math и cmath , вы уже знаете, что есть несколько общих имен, которые определены в обоих этих модулях, но применяются к действительным и комплексным числам соответственно.

Поскольку мы импортировали модуль cmath после математического модуля, он перезаписывает определения функций этих общих функций из математического модуля. Вот почему первый log10(125) возвращает действительное число, а второй log10(125) возвращает комплексное число. Теперь вы не можете использовать функцию log10() из математического модуля. Даже если вы попытались math.log10(125) , вы получите исключение NameError, потому что math фактически не существует в пространстве имен.

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

  • from module import nameA, nameB : если вы знаете, что собираетесь использовать только одно или два имени из модуля, вы можете импортировать их напрямую, используя этот метод. Таким образом, вы можете написать код более кратко, сохраняя при этом загрязнение пространства имен до минимума. Однако имейте в виду, что вы все равно не можете использовать любое другое имя из модуля, используя module.nameZ . Любая функция с таким же именем в вашей программе также перезапишет определение этой функции, импортированной из модуля. Это сделает импортированную функцию непригодной для использования. Вот пример использования этого метода:
1
2
3
4
5
6
7
8
9
dir()
# [‘__builtins__’ … ‘__spec__’]
 
from math import log2, log10
dir()
# [‘__builtins__’ … ‘__spec__’, ‘log10’, ‘log2’]
 
log10(125)
# 2.0969100130080562
  • import module : это самый безопасный и рекомендуемый способ импорта модуля. Единственным недостатком является то, что вам придется добавлять префикс имени модуля ко всем именам, которые вы собираетесь использовать в программе. Однако вы сможете избежать загрязнения пространства имен, а также определить функции, имена которых соответствуют именам функций из модуля.
1
2
3
4
5
6
7
8
9
dir()
# [‘__builtins__’ … ‘__spec__’]
 
import math
dir()
# [‘__builtins__’ … ‘__spec__’, ‘math’]
 
math.log10(125)
# 2.0969100130080562

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

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

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

Изучите Python с нашим полным руководством по питону, независимо от того, начинаете ли вы или начинающий программист, ищущий новые навыки.