Членство в классе Python и абсолютные пути

Я работаю над проектом, в котором исходный код вложен в следующую структуру:

|-- 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']

Не могли бы вы попробовать заменить MyClass на type(self) или просто self? Хотя немного грязно

Charalamm 26.10.2022 04:15

@Charalamm спасибо за предложение! Я понял, что я не ясно объяснил, поэтому я переписал вопрос

mr_faulty 26.10.2022 21:37

Вам нужно включить полный минимальный воспроизводимый пример, чтобы мы могли помочь здесь; для вещей, связанных с разрешением импорта, нам нужно будет увидеть ваши утверждения import и значение sys.path.

ShadowRanger 26.10.2022 21:39

Как вы вообще получили два разных класса с именем MyOtherClass?

chepner 26.10.2022 22:33

@chepner Извините за путаницу, но есть только один класс MyOtherClass. Я добавил больше информации в свой пост.

mr_faulty 28.10.2022 15:15

@ShadowRanger спасибо за ваш отзыв! Я добавил операторы импорта и значение sys.path.

mr_faulty 28.10.2022 15:16

@mr_faulty: Да, отредактировав их, проблема очевидна. Просто OOC, была ли моя психическая отладка правильной, и вы копаетесь в пути к sys.path через переменную среды PYTHONPATH, или это было что-то еще?

ShadowRanger 28.10.2022 19:28

@ShadowRanger большое спасибо, ваша психическая отладка верна! просто из интереса, не могли бы вы порекомендовать какую-нибудь документацию по переменной PYTHONPATH?

mr_faulty 28.10.2022 21:10
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
9
53
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Разместив свои 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.

Возможны два исправления:

  1. Измените импорт, чтобы удалить префикс package., например:

    from package.src.module1 import MyClass
    from package.src.module2 import MyOtherClass
    

    становится:

    from src.module1 import MyClass
    from src.module2 import MyOtherClass
    

    Вероятно, это не то, что вы хотите, так как package является логической частью имени модуля, поэтому мы идем к другому варианту.

  2. Измените все, что вы делаете, чтобы изменить 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).

Другие вопросы по теме