модуль повторно импортируется из другого пути

в большом приложении я работаю, несколько человек импортируют одни и те же модули по-разному, например импорт х или из y импорт x побочные эффекты этого x импортируется дважды и может ввести очень тонкие ошибки, если кто-то полагается на глобальные атрибуты

например, предположим, что у меня есть пакет mypakcage с тремя файлами mymodule.py, main.py и init.py

содержание mymodule.py

l = []
class A(object): pass

main.py содержание

def add(x):
    from mypackage import mymodule
    mymodule.l.append(x)
    print "updated list",mymodule.l

def get():
    import mymodule
    return mymodule.l

add(1)
print "lets check",get()

add(1)
print "lets check again",get()

печати

updated list [1]
lets check []
updated list [1, 1]
lets check again []

потому что теперь есть два списка в двух разных модулях, аналогично класс A отличается Для меня это выглядит достаточно серьезно, потому что сами классы будут рассматриваться по-разному например, ниже код печатает False

def create():
    from mypackage import mymodule
    return mymodule.A()

def check(a):
    import mymodule
    return isinstance(a, mymodule.A)

print check(create())

вопрос:

есть ли способ избежать этого? за исключением того, что этот модуль должен быть импортирован в один конец onyl. Не может ли это быть обработано механизмом импорта python, I видели несколько ошибок, связанных с этим в коде django и в других местах.

2 ответов


Я могу только повторить это, если main.py это файл, который вы на самом деле работает. В этом случае вы получите текущий каталог main.py на пути sys. Но у вас, по-видимому, также есть системный путь, чтобы mypackage можно было импортировать.

Python в этой ситуации не поймет, что mymodule и mypackage.mymodule-это тот же модуль, и вы получаете этот эффект. Это изменение иллюстрирует следующее:

def add(x):
    from mypackage import mymodule
    print "mypackage.mymodule path", mymodule
    mymodule.l.append(x)
    print "updated list",mymodule.l

def get():
    import mymodule
    print "mymodule path", mymodule
    return mymodule.l

add(1)
print "lets check",get()

add(1)
print "lets check again",get()


$ export PYTHONPATH=.
$ python  mypackage/main.py 

mypackage.mymodule path <module 'mypackage.mymodule' from '/tmp/mypackage/mymodule.pyc'>
mymodule path <module 'mymodule' from '/tmp/mypackage/mymodule.pyc'>

но добавьте еще один mainfile в currect каталог:

realmain.py:
from mypackage import main

и результат разный:

mypackage.mymodule path <module 'mypackage.mymodule' from '/tmp/mypackage/mymodule.pyc'>
mymodule path <module 'mypackage.mymodule' from '/tmp/mypackage/mymodule.pyc'>

поэтому я подозреваю, что у вас есть основной файл python в пакете. И в этом случае решение не делать этого. :-)


каждое пространство имен модуля импортируется только один раз. Проблема в том, что вы импортируете их по-другому. На первом вы импортируете из глобального пакета, а на втором вы делаете локальный, не упакованный import. Python видит модули как разные. Первый импорт внутренне кэшируется как mypackage.mymodule и второй mymodule только.

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

def add(x):
    from mypackage import mymodule
    mymodule.l.append(x)
    print "updated list",mymodule.l

def get():
    from mypackage import mymodule
    return mymodule.l

помните, что ваша точка входа (файл, который вы запускаете,main.py) также должны быть за пределами пакета. Если вы хотите, чтобы код точки входа находился внутри пакета, обычно вместо этого используется небольшой скрипт. Пример:

runme.py, вне пакета:

from mypackage.main import main
main()

и main.py добавить:

def main():
    # your code

найти документ Jp Calderone, чтобы быть отличным советом о том, как чтобы (не) структурировать ваш проект python. После этого у вас не будет проблем. Обратите внимание на bin папка-она находится вне пакета. Я воспроизведу весь текст здесь:

структура файловой системы проекта Python

Do:

  • назовите каталог что-нибудь связано с вашим проектом. Например, если ваш проект называется " Twisted", назовите каталог верхнего уровня для его исходные файлы Twisted. Когда вы делаете выпуски, вы должны включить версию суффикс числа:Twisted-2.5.
  • создать каталог Twisted/bin и поместите свои исполняемые файлы туда, если вы есть. Не давайте им .py расширение, даже если они Python исходный файл. Не вводи код. им кроме ввоза и призыва основная функция, определенная где-то еще в проекте.
  • если ваш проект это expressable как один питон источник файл, затем поместите его в каталог и назовите его чем-нибудь связано с вашим проектом. Например, Twisted/twisted.py. Если вам нужно несколько исходных файлов, создать пакет вместо (Twisted/twisted/, с пустым Twisted/twisted/__init__.py) и поместите в него исходные файлы. Для образец, Twisted/twisted/internet.py.
  • put модульные тесты в субпакете пакет (Примечание - это означает, что один параметр исходного файла Python выше был трюк - вам всегда нужно на хотя бы один файл для вашего блок тесты.) Например, Twisted/twisted/test/. Конечно, сделайте его пакетом с Twisted/twisted/test/__init__.py. Поместите тесты в такие файлы, как Twisted/twisted/test/test_internet.py.
  • добавить Twisted/README и Тwisted/setup.py объяснить и установите программное обеспечение, соответственно, если ты хорошо себя чувствуешь.

не:

  • поставил свой источник в каталог называется src или lib. Это делает его трудно работать без установки.
  • put ваш тест вне вашего Python пакет. Это затрудняет управление тесты против установленной версии.
  • создайте пакет, который имеет только __init__.py а затем положите все ваши код в __init__.py. Просто сделать модуль вместо пакета, это упроститься.
  • попробуйте придумать волшебный писак заставить Python состоянии импортируйте модуль или пакет без добавление пользователем каталога содержащее его в их пути импорта (либо через PYTHONPATH или некоторые другой механизм.) Вы не правильно обрабатывать все случаи и пользователи получат злость на вас, когда ваше программное обеспечение не работает в их среде.