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