модуль повторно импортируется из другого пути
в большом приложении я работаю, несколько человек импортируют одни и те же модули по-разному, например импорт х или из 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
или некоторые другой механизм.) Вы не правильно обрабатывать все случаи и пользователи получат злость на вас, когда ваше программное обеспечение не работает в их среде.