19

好的,我有两个模块,每个模块都包含一个类,问题是它们的类相互引用。

比如说,我有一个房间模块和一个包含 CRoom 和 CPerson 的人员模块。

CRoom 类包含有关房间的信息,以及房间中每个人的 CPerson 列表。

然而,CPerson 类有时需要将 CRoom 类用于其所在的房间,例如寻找门,或者查看房间里还有谁。

问题是两个模块相互导入我只是收到一个导入错误,第二个导入的是哪个模块:(

在 c++ 中,我可以通过仅包含标头来解决此问题,并且由于在这两种情况下,类都只有指向另一个类的指针,因此前向声明对于标头就足够了,例如:

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

除了将两个类放在同一个模块或类似的东西中之外,是否有在 python 中执行此操作?

编辑:添加了显示使用上述类的问题的 python 示例

错误:

回溯(最后一次调用):
文件“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: 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]

人.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)
4

5 回答 5

21

无需导入 C Room

你不使用CRoomin person.py,所以不要导入它。由于动态绑定,Python 不需要“在编译时查看所有类定义”。

如果您确实使用in CRoomperson.py则更from room import CRoom改为import room并使用 module-qualified form room.CRoom。有关详细信息,请参阅Effbot 的循环导入

旁注:你可能有一个错误Self.NextId += 1。它递增NextId实例,而不是NextId类。增加类的计数器使用CRoom.NextId += 1or Self.__class__.NextId += 1

于 2008-10-01T16:11:41.477 回答
7

你真的需要在类定义时引用这些类吗?IE。

 class CRoom(object):
     person = CPerson("a person")

或者(更有可能),您是否只需要在您的类的方法中使用 CPerson(反之亦然)。例如:

class CRoom(object):
    def getPerson(self): return CPerson("someone")

如果是第二个,则没有问题 - 因为当方法被调用而不是定义时,模块将被导入。您唯一的问题是如何引用它。您可能正在做类似的事情:

from CRoom import CPerson # or even import *

使用循环引用模块,您不能这样做,因为在一个模块导入另一个模块时,原始模块主体不会完成执行,因此命名空间将不完整。相反,使用合格的引用。IE:

#croom.py
import cperson
class CRoom(object):
    def getPerson(self): return cperson.CPerson("someone")

在这里,python 不需要在名称空间上查找属性,直到方法实际被调用,此时两个模块都应该完成它们的初始化。

于 2008-10-01T15:52:34.330 回答
3

首先,用大写字母命名你的参数是令人困惑的。由于 Python 没有正式的静态类型检查,我们使用UpperCase来表示类和lowerCase参数。

其次,我们不理会 C Room 和 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
于 2008-10-01T16:30:26.470 回答
1

你可以给第二个起别名。

import CRoom

CPerson = CRoom.CPerson
于 2008-10-01T15:53:19.257 回答
0

@S.Lott 如果我没有将任何内容导入房间模块,我会收到一个未定义的错误(我将它导入到主模块中,就像你展示的那样)

回溯(最后一次调用):
文件“C:\Projects\python\test\main.py”,第 6 行,在
Ben = Room.AddPerson('Ben', 'Blacker', 'Male')
文件“C: \Projects\python\test\room.py",第 12 行,在 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()
于 2008-10-01T16:55:17.220 回答