Вопрос

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

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

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

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

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

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

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

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

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

ошибка:

Traceback (последний вызов последний):
Файл «C:\Projects\python est\main.py», строка 1, в
из импорта помещений CRoom
Файл «C:\Projects\python est oom.py», строка 1, в
от человека импортировать Cperson
Файл «C:\Projects\python est\person.py», строка 1, в
из импорта помещений CRoom
Ошибка импорта:невозможно импортировать имя CRoom
комната.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)
Это было полезно?

Решение

Нет необходимости импортировать 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.

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

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

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

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

Во-вторых, мы не заморачиваемся с CRoom и CPerson.Верхнего регистра достаточно, чтобы указать, что это класс.Буква С не используется. 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

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

import CRoom

CPerson = CRoom.CPerson

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

Traceback (последний вызов последний):
Файл «C:\Projects\python est\main.py», строка 6, в
Бен = Room.AddPerson('Бен', 'Блэкер', 'Мужчина')
Файл «C:\Projects\python est oom.py», строка 12, в AddPerson
Человек = CPerson(Имя,ВтороеИмя,Пол,Идентификатор)
ИмяОшибка:глобальное имя «CPerson» не определено

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

РЕДАКТИРОВАТЬ: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()
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top