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

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

16 голосов
/ 01 октября 2008

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

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

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

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

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

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

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
Ошибка импорта: невозможно импортировать имя 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)

Ответы [ 5 ]

19 голосов
/ 01 октября 2008

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

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

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

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

7 голосов
/ 01 октября 2008

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

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

2 голосов
/ 01 октября 2008

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

Вот 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
1 голос
/ 01 октября 2008

Вы можете просто псевдоним второго.

import CRoom

CPerson = CRoom.CPerson
0 голосов
/ 01 октября 2008

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

Traceback (последний последний вызов):
Файл "C: \ Projects \ python \ test \ main.py", строка 6, в
Бен = Room.AddPerson («Бен», «Черный», «Мужской»)
Файл "C: \ Projects \ python \ test \ room.py", строка 12, в AddPerson
Персона = CPerson (Имя, Имя, Пол, Идентификатор)
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()
...