Зависимость модуля Python

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

Скажем, например, у меня был модуль комнаты и модуль человека, содержащий CRoom и CPerson.

Класс CRoom содержит информацию о комнате и список CPerson всех, кто находится в комнате.

Однако классу CPerson иногда необходимо использовать класс CRoom для комнаты, в которой он находится, например, чтобы найти дверь или посмотреть, кто еще находится в комнате.

Проблема в том, что два модуля импортируют друг друга, я просто получаю сообщение об ошибке импорта, которое когда-либо импортируется вторым :(

В С ++ я мог бы решить эту проблему, включив только заголовки, и поскольку в обоих случаях классы имеют только указатели на другой класс, для заголовка будет достаточно прямого объявления, например:

class CPerson;//forward declare
class CRoom
{
    std::set<CPerson*> People;
    ...

Есть ли способ сделать это в python, кроме размещения обоих классов в одном модуле или чего-то в этом роде?

edit: добавлен пример Python, показывающий проблему с использованием вышеуказанных классов

ошибка:

Traceback (most recent call last):
File "C:\Projects\python\test\main.py", line 1, in
from room import CRoom
File "C:\Projects\python\test\room.py", line 1, in
from person import CPerson
File "C:\Projects\python\test\person.py", line 1, in
from room import CRoom
ImportError: cannot import name CRoom
room.py

from person import CPerson

class CRoom:
    def __init__(Self):
        Self.People = {}
        Self.NextId = 0

    def AddPerson(Self, FirstName, SecondName, Gender):
        Id = Self.NextId
        Self.NextId += 1#

        Person = CPerson(FirstName,SecondName,Gender,Id)
        Self.People[Id] = Person
        return Person

    def FindDoorAndLeave(Self, PersonId):
        del Self.People[PeopleId]

person.py

from room import CRoom

class CPerson:
    def __init__(Self, Room, FirstName, SecondName, Gender, Id):
        Self.Room = Room
        Self.FirstName = FirstName
        Self.SecondName = SecondName
        Self.Gender = Gender
        Self.Id = Id

    def Leave(Self):
        Self.Room.FindDoorAndLeave(Self.Id)

Можете ли вы опубликовать небольшой тестовый пример, который воспроизводит вашу ошибку? Я попытался создать два модуля, которые ссылаются друг на друга, и у меня не было проблем, поэтому я предполагаю, что есть некоторая тонкость, которую мне не хватает.

Dave Costa 01.10.2008 19:50

[offtop] Пожалуйста, прочтите руководство по стилю Python python.org/dev/peps/pep-0008. В частности, удалите первую букву «C» из имен классов, все остальные имена в вашем примере должны быть в нижнем регистре. Чтобы ответить на ваш вопрос: просто import room, а в методах Person используется room.Room(...).

jfs 01.10.2008 21:46

Было бы полезно указать, какие версии Python вы используете. Я не думаю, что это проблема для какой-то версии python 3 (я думаю, 3.5, но не 3.4).

Charlie Parker 08.02.2017 18:13
Почему в 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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
17
3
14 070
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Вам действительно нужно ссылаться на классы во время определения класса? т.е.

 class CRoom(object):
     person = CPerson("a person")

Или (что более вероятно) вам просто нужно использовать CPerson в методах вашего класса (и наоборот). например:

class CRoom(object):
    def getPerson(self): return CPerson("someone")

Если второе, проблем нет - поскольку к тому времени, когда метод получит называется, а не определен, модуль будет импортирован. Ваша единственная проблема в том, как к нему относиться. Скорее всего, вы делаете что-то вроде:

from CRoom import CPerson # or even import *

С модулями с циклическими ссылками вы не можете этого сделать, поскольку в момент, когда один модуль импортирует другой, исходное тело модуля не завершает выполнение, поэтому пространство имен будет неполным. Вместо этого используйте квалифицированные ссылки. то есть:

#croom.py
import cperson
class CRoom(object):
    def getPerson(self): return cperson.CPerson("someone")

Здесь python не нужно искать атрибут в пространстве имен до тех пор, пока метод не будет вызван, и к этому времени оба модуля должны завершить свою инициализацию.

Вы можете просто указать второй псевдоним.

import CRoom

CPerson = CRoom.CPerson

Это не сработает, как если бы это было сделано в верхней части модуля, CRoom.CPerson может еще не существовать. Единственный способ сделать это - вставить свое имя в пространство имен других модулей из другого модуля (например, import croom; croom.CPerson = CPerson), что очень взломано. Лучше использовать полностью определенные имена.

Brian 01.10.2008 20:13
Ответ принят как подходящий

Не нужно импортировать CRoom

Вы не используете CRoom в person.py, поэтому не импортируйте его. Благодаря динамической привязке Python не нужно «видеть все определения классов во время компиляции».

Если вы на самом деле делать используете CRoom в person.py, измените from room import CRoom на import room и используйте форму room.CRoom с указанием модуля. Подробнее см. Циркулярный импорт Effbot.

Примечание: у вас, вероятно, ошибка в строке Self.NextId += 1. Он увеличивает NextId экземпляра, а не NextId класса. Для увеличения счетчика класса используйте CRoom.NextId += 1 или Self.__class__.NextId += 1.

Так как же человек должен видеть, кто еще находится в комнате, или находить дверь и т. д., Если у него нет ссылки на комнату, в которой он находится?

Fire Lancer 01.10.2008 20:16

Просто уберите "из комнаты импорт CRoom". Вы по-прежнему сможете передавать экземпляры CRoom в person.py извне. Вы не сможете вызвать конструктор CRoom из person.py, но вы все равно этого не делаете.

Constantin 01.10.2008 20:30

Второе предложение не работает. Вы импортируете модуль, а не класс, поэтому вы получите AttributeError: 'module' object has no attribute.

Rémi Benoit 16.09.2016 13:09

Во-первых, название аргументов заглавными буквами сбивает с толку. Поскольку в Python нет формальной статической проверки типов, мы используем UpperCase для обозначения класса и lowerCase для обозначения аргумента.

Во-вторых, мы не беспокоимся о CRoom и CPerson. Для обозначения класса достаточно верхнего регистра. Буква C не используется. Room. Person.

В-третьих, мы обычно не помещаем вещи в формат Один класс на файл. Файл - это модуль Python, и мы чаще импортируем целый модуль со всеми классами и функциями.

[Я знаю, что это привычки - сегодня вам не нужно отказываться от них, но они затрудняют чтение.]

Python не использует статически определенные типы, такие как C++. Когда вы определяете функцию-метод, вы формально не определяете тип данных аргументов этой функции. Вы просто перечисляете имена некоторых переменных. Надеюсь, клиентский класс предоставит аргументы правильного типа.

Во время выполнения, когда вы делаете запрос метода, Python должен быть уверен, что у объекта есть метод. ПРИМЕЧАНИЕ. Python не проверяет, является ли объект правильным типом - это не имеет значения. Он только проверяет, есть ли у него правильный метод.

Петля между room.Room и person.Person является проблемой. Вам не нужно включать одно при определении другого.

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

Вот room.py

import person
class Room( object ):
    def __init__( self ):
        self.nextId= 0
        self.people= {}
    def addPerson(self, firstName, secondName, gender):
        id= self.NextId
        self.nextId += 1

        thePerson = person.Person(firstName,secondName,gender,id)
        self.people[id] = thePerson
        return thePerson 

Работает нормально, если в конечном итоге Person определен в пространстве имен, в котором он выполняется. При определении класса личность не обязательно должна быть известна.

Человек не обязательно должен быть известен до тех пор, пока не будет вычислено выражение Person (...) во время выполнения.

Вот person.py

import room
class Person( object ):
    def something( self, x, y ):
        aRoom= room.Room( )
        aRoom.addPerson( self.firstName, self.lastName, self.gender )

Ваш main.py выглядит так

import room
import person
r = room.Room( ... )
r.addPerson( "some", "name", "M" )
print r

@ S.Lott если я ничего не импортирую в модуль комнаты, вместо этого я получаю неопределенную ошибку (я импортировал ее в основной модуль, как вы показали)

Traceback (most recent call last):
File "C:\Projects\python\test\main.py", line 6, in
Ben = Room.AddPerson('Ben', 'Blacker', 'Male')
File "C:\Projects\python\test\room.py", line 12, in AddPerson
Person = CPerson(FirstName,SecondName,Gender,Id)
NameError: global name 'CPerson' is not defined

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

Обновлено: main.py

from room import CRoom
from person import CPerson

Room = CRoom()

Ben = Room.AddPerson('Ben', 'Blacker', 'Male')
Tom = Room.AddPerson('Tom', 'Smith',   'Male')

Ben.Leave()

Основная причина ошибки в main. Как выглядит главное? Импортирует ли он номер и человека?

S.Lott 01.10.2008 21:02

Спасибо. Моя ошибка в правилах применения. Каждый модуль имеет собственное пространство имен. Комната выполняется в пространстве имен модуля комнаты.

S.Lott 01.10.2008 21:26

Почему основной импорт CPerson? Он его не использует. Если вы создаете людей только через экземпляры Room, вам нужно только импортировать person в room.py.

John Fouhy 02.10.2008 06:15

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