Хорошо, у меня есть два модуля, каждый из которых содержит класс, проблема в том, что их классы ссылаются друг на друга.
Скажем, например, у меня был модуль комнаты и модуль человека, содержащий 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)
[offtop] Пожалуйста, прочтите руководство по стилю Python python.org/dev/peps/pep-0008. В частности, удалите первую букву «C» из имен классов, все остальные имена в вашем примере должны быть в нижнем регистре. Чтобы ответить на ваш вопрос: просто import room, а в методах Person используется room.Room(...).
Было бы полезно указать, какие версии Python вы используете. Я не думаю, что это проблема для какой-то версии python 3 (я думаю, 3.5, но не 3.4).






Вам действительно нужно ссылаться на классы во время определения класса? т.е.
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), что очень взломано. Лучше использовать полностью определенные имена.
Не нужно импортировать 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.
Так как же человек должен видеть, кто еще находится в комнате, или находить дверь и т. д., Если у него нет ссылки на комнату, в которой он находится?
Просто уберите "из комнаты импорт CRoom". Вы по-прежнему сможете передавать экземпляры CRoom в person.py извне. Вы не сможете вызвать конструктор CRoom из person.py, но вы все равно этого не делаете.
Второе предложение не работает. Вы импортируете модуль, а не класс, поэтому вы получите AttributeError: 'module' object has no attribute.
Во-первых, название аргументов заглавными буквами сбивает с толку. Поскольку в 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. Как выглядит главное? Импортирует ли он номер и человека?
Спасибо. Моя ошибка в правилах применения. Каждый модуль имеет собственное пространство имен. Комната выполняется в пространстве имен модуля комнаты.
Почему основной импорт CPerson? Он его не использует. Если вы создаете людей только через экземпляры Room, вам нужно только импортировать person в room.py.
Можете ли вы опубликовать небольшой тестовый пример, который воспроизводит вашу ошибку? Я попытался создать два модуля, которые ссылаются друг на друга, и у меня не было проблем, поэтому я предполагаю, что есть некоторая тонкость, которую мне не хватает.