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

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

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

В классе CRoom содержится информация о комнате и список CPerson каждого в комнате.

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

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

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

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

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

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

Ошибка:

Traceback (последний последний звонок):
 Файл "C:\Projects\python\test\main.py", строка 1, на
   из комнаты импорт CRoom
 Файл "C:\Projects\python\test\room.py", строка 1, на
   от лица импорта CPerson
 Файл "C:\Projects\python\test\person.py", строка 1, на
   из комнаты импорт CRoom
ImportError: невозможно импортировать имя 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)

Ответ 1

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

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

Если вы действительно используете CRoom в person.py, затем измените from room import CRoom на import room и используйте форму, соответствующую модулю room.CRoom. Подробнее см. Circular Imports.

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

Ответ 2

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

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

Ответ 3

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

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

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

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

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

Во время выполнения, когда вы делаете запрос метода, 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

Ответ 4

Вы могли бы просто написать второй.

import CRoom

CPerson = CRoom.CPerson

Ответ 5

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

Traceback (последний последний звонок):
 Файл "C:\Projects\python\test\main.py", строка 6, на
   Ben = Room.AddPerson( "Бен", "Черный", "Мужской" )
 Файл "C:\Projects\python\test\room.py", строка 12, в AddPerson
   Person = CPerson (FirstName, SecondName, Gender, Id)
NameError: глобальное имя "CPerson" не определено

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

EDIT: 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()