2008-10-01 3 views
16

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

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

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

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

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

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

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

есть в любом случае сделать это в 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) 
+0

Вы можете разместить небольшой тест, который воспроизводит вашу ошибку? Я попытался создать два модуля, которые ссылаются друг на друга и не имеют проблем, поэтому я предполагаю, что есть некоторые тонкие моменты, которые мне не хватает. – 2008-10-01 15:50:15

+2

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

+0

Возможно, было бы полезно указать, какие версии python вы используете. Я не думаю, что это проблема для некоторой версии python 3 (я думаю, 3.5, но не 3.4). – 2017-02-08 15:13:50

ответ

18

Нет необходимости импортировать Крум

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

Если вы на самом деле сделать использования CRoom в person.py, а затем изменить from room import CRoom к import room и использовать модуль квалифицированной формы room.CRoom. См. Effbot's Circular Imports.

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

7

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

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") 

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

1

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

import CRoom 

CPerson = CRoom.CPerson 
+0

Это не сработает, как если бы это было сделано на модуле, CRoom.CPerson еще не существует. Единственный способ, которым вы могли бы это сделать, - это высунуть свое имя в другое пространство имен модулей из другого модуля (например, import croom; croom.CPerson = CPerson), который очень взломан. Лучше использовать полностью квалифицированные имена. – Brian 2008-10-01 16:13:53

2

Во-первых, наименования ваших аргументов прописными буквами сбивают с толку. Поскольку у 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.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 
0

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

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

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

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