Пространства имен с импортом модулей - программирование
Подтвердить что ты не робот

Пространства имен с импортом модулей

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

main.py:

from math import *
import module1

def wow():

    print pi


wow()
module1.cool()

module1.py:

def cool():

    print pi

При запуске main.py я получаю:

3.14159265359

Traceback (most recent call last):
  File "Z:\Python\main.py", line 10, in <module>
    module1.cool()
  File "Z:\Python\module1.py", line 3, in cool
    print pi
NameError: global name 'pi' is not defined

То, что мне трудно понять, - это то, почему я получаю ошибку имени при запуске main.py. Я знаю, что переменная pi становится глобальной для основного модуля при импорте, потому что wow может получить к ней доступ. Я также знаю, что cool становится глобальным для основного модуля при импорте, потому что я могу распечатать module1.cool и получить <function cool at 0x02B11AF0>. Так как cool находится внутри глобального пространства имен основного модуля, программа не должна сначала заходить в функцию cool для переменной pi, а затем, когда она ее не найдет, загляните внутрь main для переменной pi и найти ее там?

Единственный способ обойти это, о котором я знаю, - это импортировать математический модуль внутри module1.py. Мне не нравится идея этого, хотя, потому что это усложняет ситуацию, и я поклонник приятного простого кода. Я чувствую, что я близок к пониманию пространств имен, но мне нужна помощь в этом. Спасибо.

4b9b3361

Ответ 1

Как показывает трассировка, проблема не в main.py, а в module1.py:

Traceback (most recent call last):
  File "Z:\Python\main.py", line 10, in <module>
    module1.cool()
  File "Z:\Python\module1.py", line 3, in cool
    print pi
NameError: global name 'pi' is not defined

Другими словами, в module1 глобальное имя pi отсутствует, потому что вы его там не импортировали. Когда вы выполняете from math import * в main.py, это просто импортирует все из пространства имен модулей math в пространство имен модулей main, а не в каждое пространство имен модулей.

Я думаю, что ключевое, что вам не хватает здесь, это то, что каждый модуль имеет свое "глобальное" пространство имен. Сначала это может быть немного запутанным, потому что на таких языках, как C, существует одно глобальное пространство имен, разделяемое всеми переменными и функциями extern. Но как только вы преодолеете это предположение, путь Python имеет смысл.

Итак, если вы хотите использовать pi из module1, вам нужно сделать from math import * в module1.py. (Или вы могли бы найти какой-либо другой способ его вставить - например, module1.py мог делать from main import *, или main.py мог делать module1.pi = pi и т.д. Или вы могли бы вставить pi в волшебство builtins/__builtin__ или использовать другие трюки. Но очевидным решением является сделать import, где вы хотите импортировать его.)


В качестве примечания вы обычно не хотите делать from foo import * где угодно, кроме интерактивного интерпретатора, или, иногда, верхнего уровня script. Существуют исключения (например, несколько модулей явно предназначены для использования таким образом), но эмпирическое правило относится либо к import foo, либо к ограниченному from foo import bar, baz.

Ответ 2

"Явное лучше, чем неявное" - это дизайнерское решение, созданное создателями Python (запуск python и запуск import this).

Поэтому, когда вы запускаете module1.cool(), Python не будет искать undefined pi в модуле main.


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

Кроме того, вам следует избегать импорта from X import * -style, что тоже плохо. Здесь вы можете сделать: from math import pi.

Ответ 3

Как говорили другие, на вашем module1 на самом деле нет глобального pi. Хорошим решением для вас является то, что только один из pi импортирует только math и явно гарантирует, что pi, который вы получаете, - это один из module1:

main.py:

import module1

def wow():
    print module1.pi

wow()
module1.cool()

module1.py:

from math import pi

def cool():
    print pi

Ответ 4

Простой подход exec (python 3) или execfile (python 2), упомянутый в комментариях @abarnert, может быть полезен для некоторых рабочих процессов. Все, что нужно, это заменить строку импорта:

exec( open("module1.py").read() )       # python 3

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

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

Но для простых случаев (например, использование Python в качестве сценария оболочки) exec предоставляет простой и лаконичный способ скрытия общих функций, позволяя им совместно использовать глобальное пространство имен. Просто обратите внимание, что в этом случае вы можете подумать над тем, как назвать свои функции (например, используйте v1_cool и v2_cool для отслеживания различных версий, поскольку вы не можете использовать v1.cool и v2.cool).

Один менее очевидный недостаток использования exec здесь заключается в том, что ошибки в исполняемом коде могут не отображать номер строки ошибки, хотя вы можете обойти это: как получить номер строки ошибки из exec или execfile в Python

Ответ 5

Внутри модуля вы можете просто определить from math import pi, который будет импортировать только pi из математики, но не весь математический модуль.