Импорт модуля из функции в __init__.py привязывает объект модуля к глобальному пространству имен? - PullRequest
0 голосов
/ 11 января 2019

Я новичок в Python, и у меня есть сомнения по поводу того, как объекты модуля связаны с пространством имен пакета __init__.py. Я изложу проблему более четко с некоторым кодом.


Предположим, есть пакет с именем myPkg, содержащий два модуля: firstMod и secondMod:

myPkg\
     __init__.py
     firstMod.py
     secondMod.py

Файл __init__.py имеет следующий вид:

def myFun():
    from . import firstMod as fm

myFun()

Файл firstMod пуст.

Файл secondMod имеет следующий вид:

def myFun():
    from . import firstMod as fm

myFun()

Теперь запустите интерпретатор Python 2.7.15 в том же каталоге, что и myPkg, и выполните следующие действия:

>>> import myPkg
>>> dir(myPkg)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', 'firstMod', 'myFun']
>>> from myPkg import secondMod
>>> dir(secondMod)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'myFun']
>>>

Хотя ожидался результат dir(secondMod), результат dir(myPkg) оказался неожиданным (для меня). Кажется, что только для модуля __init__.py пакета имена импортированных объектов модуля связаны с глобальным пространством имен, даже если модули импортируются из функции. Это не происходит в других модулях.

Редактировать: как выясняется, это происходит только при импорте модулей пакета. Импорт внешних модулей из myFun не приводит к привязке имени в __init__.py.


Может кто-нибудь объяснить, почему это происходит? А также: есть ли способ избежать этого поведения?


Редактировать

Обратите внимание, что наличие 'firstMod' в глобальном пространстве имен __init__.py является свойством, которое применяется только к пакетам.

На самом деле, если один определяет два модуля:

zeroMod.py
firstMod.py

вне любой упаковки и заполняет zeroMod.py:

def myFun():
    import firstMod as fm

myFun()

интерпретатор не будет привязывать имя 'firstMod' к глобальному пространству имен zeroMod.py, даже если firstMod.py загружается впервые:

>>> import zeroMod
>>> dir(zeroMod)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'myFun']
>>>

Я не понимаю, почему привязка имени происходит для __init__.py, но не для zeroMod.py.

1 Ответ

0 голосов
/ 13 января 2019

При запуске:

import myPkg

Python загрузит файл myPkg/__init__.py и выполнит его.

Поскольку этот пакет содержит вызов функции (myFun()), он выполнит его (только один раз при первом импорте).

myFun():

def myFun():
    from . import firstMod as fm

Опять же, эта функция импортирует firstMod. Таким образом, файл myPkg/firstMod.py загружается и выполняется (ничего не делается, потому что firstMod.py пуст).

В конце импорта myPkg вы получаете следующий объект на уровне модуля:

['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', 'firstMod', 'myFun']

Затем, в том же сеансе Python, если вы запустите:

from myPkg import secondMod

Python не будет загружать файл myPkg/__init__.py во второй раз, потому что модуль уже загружен.

Python загрузит файл myPkg/secondMod.py и выполнит его.

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

myFun() import firstMod тоже:

def myFun():
    from . import firstMod as fm

firstMod уже импортирован, поэтому вы не найдете его в своем модуле secondMod:

['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'myFun']

Вещи, которые вы должны учитывать:

  • вы должны рассмотреть следующие правила PEP8 .

  • Модули одноэлементные (даже если в редких случаях вы можете перезагрузить модуль),

  • Если вы не хотите запускать код в своем модуле, используйте классический подход:

Например:

if __name__ == "__main__":
    myFun()
...