19

それぞれがクラスを含む 2 つのモジュールがあります。問題は、それらのクラスが相互に参照していることです。

たとえば、room モジュールと CRoom と CPerson を含む person モジュールがあるとします。

CRoom クラスには、ルームに関する情報と、ルーム内のすべての人の CPerson リストが含まれています。

ただし、CPerson クラスは、たとえばドアを見つけたり、部屋に他の人がいるかどうかを確認したりするために、その部屋に CRoom クラスを使用する必要がある場合があります。

問題は、2 つのモジュールが相互にインポートしていることです。2 番目にインポートされたモジュールでインポート エラーが発生します :(

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

5 に答える 5

21

CRoomをインポートする必要はありません

では使用CRoomしないためperson.py、インポートしないでください。動的バインディングにより、Python は「コンパイル時にすべてのクラス定義を見る」必要はありません。

実際に in を使用する場合は、モジュール修飾された形式に変更して使用してください。詳細については、 Effbot の循環インポートを参照してください。CRoomperson.pyfrom room import CRoomimport roomroom.CRoom

補足:おそらくSelf.NextId += 1行にエラーがあります。クラスNextIdではなく、インスタンスをインクリメントします。NextIdクラスのカウンタをインクリメントするにはCRoom.NextId += 1、 またはを使用しますSelf.__class__.NextId += 1

于 2008-10-01T16:11:41.477 に答える
7

クラス定義時に実際にクラスを参照する必要がありますか? すなわち。

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

または (より可能性が高い)、クラスのメソッドで CPerson を使用する必要があるだけですか (逆も同様です)。例えば:

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

2 番目の場合、問題はありません。メソッドが定義されるのではなく呼び出されるまでに、モジュールがインポートされます。あなたの唯一の問題は、それを参照する方法です。おそらくあなたは次のようなことをしています:

from CRoom import CPerson # or even import *

モジュールの循環参照では、これを行うことはできません。あるモジュールが別のモジュールをインポートする時点で、元のモジュール本体の実行が完了していないため、名前空間が不完全になります。代わりに、修飾参照を使用してください。すなわち:

#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を意味し、引数を意味するために を使用します。

2 つ目は、CRoom と CPerson を気にしないことです。クラスであることを示すには、大文字で十分です。文字 C は使用されません。 Room. Person.

第 3 に、私たちは通常、ファイルごとに 1 つのクラスの形式で物事を配置しません。ファイルは 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

2番目のものにエイリアスを付けることができます。

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: グローバル名 '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()
于 2008-10-01T16:55:17.220 に答える