Я работаю над проектом, в котором исходный код вложен в следующую структуру:
|-- my-project
|-- test_notebook.ipynb
|-- package
|-- src
| |-- module1.py #defines MyClass
| |-- module2.py #defines MyOtherClass
| |-- some_other_modules.py
| |-- __init__.py
|-- tests
|-- some_modules.py
Теперь проблема заключается в следующем. my_method в module1.py - это метод MyClass. my_method выглядит следующим образом:
from src.module2 import MyOtherClass
class MyClass:
def my_method(self, list_of_objects: list):
if all(isinstance(obj, str) for obj in list_of_objects):
#do something
elif all(isinstance(obj, MyOtherClass) for obj in list_of_objects):
#do something else
else:
raise TypeError("This is a test")
Если я создам список MyOtherClass экземпляров o внутри самого module1 и протестирую my_method, то он будет работать так, как ожидалось: условие elif выполнено. Однако, если я создам тот же список экземпляров в test_notebook.ipynb, то класс o будет <class package.src.module2.MyOtherClass>. Таким образом, если я вызываю my_method, то возникает TypeError: Python (я использую версию 3.10) считает <class package.src.module2.MyOtherClass> отличным от <class MyOtherClass>.
from package.src.module1 import MyClass
from package.src.module2 import MyOtherClass
obj1 = MyClass()
other_class_obj_1 = MyOtherClass()
other_class_obj_2 = MyOtherClass()
other_class_obj_3 = MyOtherClass()
result = obj1.my_method([other_class_obj_1, other_class_obj_2, other_class_obj_3])
Возникает TypeError:
TypeError: This is a test
Я попытался переписать условие, используя абсолютный путь MyOtherClass, следующим образом:
elif all(isinstance(obj, package.src.module2.MyOtherClass)) for obj in list_of_objects:
#do_something
Но это не сработало, так как Python не умеет разрешать package.src.module2.MyOtherClass. Есть идеи, как это решить?
Я добавляю значение sys.path ниже:
>>> import sys
>>> print(sys.path)
['', '/Users/my_name/Documents/Python_Projects/my-project/package', '/Library/Frameworks/Python.framework/Versions/3.10/lib/python310.zip', '/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10', '/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/lib-dynload', '/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages']
@Charalamm спасибо за предложение! Я понял, что я не ясно объяснил, поэтому я переписал вопрос
Вам нужно включить полный минимальный воспроизводимый пример, чтобы мы могли помочь здесь; для вещей, связанных с разрешением импорта, нам нужно будет увидеть ваши утверждения import и значение sys.path.
Как вы вообще получили два разных класса с именем MyOtherClass?
@chepner Извините за путаницу, но есть только один класс MyOtherClass. Я добавил больше информации в свой пост.
@ShadowRanger спасибо за ваш отзыв! Я добавил операторы импорта и значение sys.path.
@mr_faulty: Да, отредактировав их, проблема очевидна. Просто OOC, была ли моя психическая отладка правильной, и вы копаетесь в пути к sys.path через переменную среды PYTHONPATH, или это было что-то еще?
@ShadowRanger большое спасибо, ваша психическая отладка верна! просто из интереса, не могли бы вы порекомендовать какую-нибудь документацию по переменной PYTHONPATH?
@mr_faulty: Нравится материал из основной документации по Python ? Это не так интересно; это просто хак, используемый вместо правильного создания устанавливаемых пакетов. Если вы хотите создать правильные устанавливаемые пакеты, простой файл setup.py — это то, что вам нужно (в конечном итоге предполагается поддержка только setup.cfg-пакетов, но они еще недостаточно документированы).






Разместив свои sys.path и import проблема ясна. Ваш sys.path после '' (который имеет особое значение, которое здесь не имеет значения) начинается с:
'/Users/my_name/Documents/Python_Projects/my-project/package'
и ни в коем случае не содержит:
'/Users/my_name/Documents/Python_Projects/my-project'
Это означает, что если вы попытаетесь импортировать с помощью import spam или from spam import eggs, и он достигнет этого компонента sys.path, не найдя его (вероятно, если он не будет найден в рабочем каталоге, что может привести к путанице; вы можете увидеть, что ваш код работает нормально, если вы запускали Python в интерактивном режиме из /Users/my_name/Documents/Python_Projects/my-project или если вы запустили скрипт, расположенный в этом каталоге), он будет искать файл с именем /Users/my_name/Documents/Python_Projects/my-project/package/spam.py (модуль) или каталог с именем /Users/my_name/Documents/Python_Projects/my-project/package/spam/ (пакет).
Ваш импорт запрашивает from package.src.module1 import MyClass, поэтому они будут найдены в /Users/my_name/Documents/Python_Projects/my-project/package только в том случае, если остальные ваши файлы находятся в подкаталоге package, также называемом package.
Возможны два исправления:
Измените импорт, чтобы удалить префикс package., например:
from package.src.module1 import MyClass
from package.src.module2 import MyOtherClass
становится:
from src.module1 import MyClass
from src.module2 import MyOtherClass
Вероятно, это не то, что вы хотите, так как package является логической частью имени модуля, поэтому мы идем к другому варианту.
Измените все, что вы делаете, чтобы изменить sys.path (будь то ручное манипулирование в скрипте Python, установка переменной среды PYTHONPATH для указания на этот каталог, взлом вашего sys.path через пользовательский файл .pth в вашем каталоге site-packages или какая-то сложная IDE конфигурация, которая дает аналогичный конечный результат), так что вместо добавления '/Users/my_name/Documents/Python_Projects/my-project/package' добавляется '/Users/my_name/Documents/Python_Projects/my-project' (без окончательного /package). Это заставит работать существующий импорт from package.src.module1 import XXX, потому что теперь он будет сканировать каталог my-project, а не каталог package, в поисках каталога с именем package (который my-project содержит, а package нет).
Психическая отладка говорит, что вы, вероятно, используете хаки PYTHONPATH (если бы вы вручную настраивали sys.argv, маловероятно, что вы не забыли поставить его после ведущей записи '', а все остальные варианты не так легко обнаружить, если вы еще не знаете система импорта наизусть), например. у тебя есть:
export PYTHONPATH = "$HOME/Documents/Python_Projects/my-project/package"
в одном из ваших файлов конфигурации оболочки (~/.bashrc, ~/.bash_profile и т. д.), или вы запустили его вручную в оболочке, из которой вы запускаете свой код, или, возможно, делаете что-то подобное в конфигурации ноутбука Jupyter. Так что найдите, где вы это делаете, и обрежьте компонент /package.
В будущем вы, вероятно, захотите сделать правильный setup.py и/или setup.cfg для своего проекта, чтобы вы могли собрать для него дистрибутив (архив с исходным кодом или колесо или что-то подобное) и правильно установить его с помощью python3 -mpip, чтобы вы не вообще не полагаться на хаки вроде PYTHONPATH (это уже не так ужасно, как было раньше, теперь, когда мало кто использует Python 2, но это все еще проблема, когда он полностью неверсирован и может быть установлен миллионом способов, поэтому, если вы непреднамеренно код, который синтаксически действителен только в некоторых версиях Python, например, любой код с переменной или функцией или чем-то еще с именем async в нем перестал быть допустимым Python в 3.7, вы должны переписать его, чтобы он был полностью переносимым, где могут быть установлены правильно установленные модули отдельно для каждой версии Python, установленной глобально или для каждого пользователя, или даже изолированной в виртуальных средах, созданных пакетами venv или virtualenv).
Не могли бы вы попробовать заменить
MyClassнаtype(self)или простоself? Хотя немного грязно