Python模块依赖性
好的,我有两个模块,每个模块包含一个类,问题是它们的类相互引用。
假设我有一个房间模块和一个包含CRoom和CPerson的人员模块。
CRoom类包含有关房间的信息,以及房间中每个房间的CPerson列表。
但是,CPerson类有时需要在其房间内使用CRoom类,例如查找门,或者也要查看房间中还有谁。
问题是两个模块互相导入,我只是收到一个导入错误,第二个被导入:(
在c ++中,我可以通过仅包含标头来解决此问题,并且由于在两种情况下,这些类仅具有指向另一个类的指针,因此向前声明就可以满足标头的需要,例如:
class CPerson;//forward declare
class CRoom
{
std::set<CPerson*> People;
...
除了将两个类都放在同一个模块中或者类似的东西之外,是否还有其他方法可以在python中执行此操作?
编辑:添加了使用上述类显示问题的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]
人
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)
解决方案
我们实际上需要在类定义时引用类吗? 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不需要在命名空间上查找属性,到那时两个模块都应该完成它们的初始化。
我们可以将第二个作为别名。
import CRoom CPerson = CRoom.CPerson
无需导入CRoom
我们不要在person.py中使用CRoom,因此不要导入它。由于动态绑定,Python不需要"在编译时查看所有类定义"。
如果我们确实在person.py中使用了CRoom,则将从房间导入CRoom更改为导入房间,并使用模块限定格式room.CRoom。有关详细信息,请参见Effbot的通函导入。
旁注:" Self.NextId + = 1"行中可能存在错误。它增加实例的" NextId",而不是类的" NextId"。要增加类的计数器,请使用CRoom.NextId + = 1或者Self .__ class __。NextId + = 1。
首先,用大写字母命名论点会造成混淆。由于Python没有形式化的静态类型检查,因此我们使用UpperCase来表示一个类,而使用lowerCase来表示一个参数。
其次,我们不必理会CRoom和CPerson。大写字母足以表明它是一类。不使用字母C。房间。 人。
第三,我们通常不会将内容放在"每个文件一个类"的格式中。文件是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
@ S.Lott
如果我未将任何内容导入到room模块中,则会收到未定义的错误(我将其导入到主模块中,如我们所示)
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
另外,使用不同模块的原因是我从容器类(例如房间)开始遇到问题的地方已经有几百行,所以我希望其中的项目(例如人)放在单独的文件中。
编辑:
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()

