好的我有两个模块,每个模块都包含一个类,问题是它们的类互相引用。
让我们说例如我有一个房间模块和一个包含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:无法导入名称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 :(得分:19)
无需导入CRoom
您不在CRoom
中使用person.py
,因此请勿导入。由于动态绑定,Python不需要“在编译时查看所有类定义”。
如果您实际 在CRoom
中使用person.py
,请将from room import CRoom
更改为import room
并使用模块限定格式{{1} }。有关详细信息,请参阅Effbot's Circular Imports。
旁注:您可能在room.CRoom
行中出错了。它增加了实例的Self.NextId += 1
,而不是类的NextId
。要增加班级的计数器使用NextId
或CRoom.NextId += 1
。
答案 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")
这里,python不需要在命名空间中查找属性,直到实际调用该方法为止,这时两个模块都应该完成初始化。
答案 2 :(得分: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。
在运行时,不必知道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
答案 3 :(得分:1)
你可以为第二个别名。
import CRoom
CPerson = CRoom.CPerson
答案 4 :(得分:0)
@美国洛特 如果我没有将任何东西导入房间模块,我会得到一个未定义的错误(我将其导入主模块,如你所示)
追踪(最近的呼叫最后):
文件“C:\ Projects \ python \ test \ main.py”,第6行,中 Ben = Room.AddPerson('Ben','Blacker','Male')
在AddPerson中输入文件“C:\ Projects \ python \ test \ room.py”,第12行 Person = CPerson(FirstName,SecondName,Gender,Id)
NameError:未定义全局名称“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()