Python模块依赖

时间:2008-10-01 15:38:58

标签: python module circular-dependency

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

让我们说例如我有一个房间模块和一个包含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)

5 个答案:

答案 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。要增加班级的计数器使用NextIdCRoom.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未使用。 RoomPerson

第三,我们通常不会将内容放在每个文件一类格式中。文件是Python模块,我们更经常导入包含所有类和函数的整个模块。

[我知道这些是习惯 - 你今天不需要打破它们,但它们确实难以阅读。]

Python不使用像C ++这样的静态定义类型。定义方法函数时,不要正式定义该函数的参数的数据类型。您只需列出一些变量名称。希望客户端类提供正确类型的参数。

在运行时,当您发出方法请求时,Python必须确保该对象具有该方法。注意。 Python不会检查对象是否是正确的类型 - 这无关紧要。它只检查它是否有正确的方法。

room.Roomperson.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()