2008-10-01 4 views
16

Ok Ich habe zwei Module, die jeweils eine Klasse enthält, das Problem ist, ihre Klassen aufeinander verweisen.Python-Modul Abhängigkeit

Hier können zum Beispiel sagen, dass ich ein Raummodul hatte und eine Person Modul Croom und CPerson enthält.

Die Croom-Klasse enthält Informationen zum Thema Raum und eine CPerson Liste eines jeden im Raum.

Die Klasse CPerson aber manchmal braucht die Croom Klasse für das Zimmer seiner in verwenden, zum Beispiel die Tür zu finden oder zu sehen, wer sonst noch im Raum ist.

Das Problem ist mit den beiden Modulen sie importieren ich einen Importfehler nur bekommen, auf der jemals zweite importiert wird :(

in C++ ich dies nur lösen könnte, indem die Header einschließlich, und da in beiden Fällen die Klassen haben nur Zeiger auf die anderen Klasse, eine Vorwärtsdeklaration für den Header zB ausreichen würde:

class CPerson;//forward declare 
class CRoom 
{ 
    std::set<CPerson*> People; 
    ... 

gibt es trotzdem, dies in python zu tun, außer beiden Klassen im gleichen Modul oder so ähnlich platzieren?

bearbeiten: added python Beispiel zeigt Problem oben Klassen

Fehler mit:

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] 

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

Können Sie sich einen kleinen Testfall schreiben, dass Ihre Fehler reproduziert? Ich habe versucht, zwei Module zu erstellen, die aufeinander verweisen und keine Probleme haben, also nehme ich an, dass es einen subtilen Punkt gibt, den ich vermisse. –

+2

[offtop] Bitte lesen Sie den Python Style Guide http://www.python.org/dev/peps/pep-0008/. Lassen Sie insbesondere das erste "C" von den Klassennamen fallen, alle anderen Namen in Ihrem Beispiel sollten in Kleinbuchstaben geschrieben sein. Um Ihre Frage zu beantworten: Verwenden Sie einfach 'Raum importieren' und in den Methoden von Personen' Raum.Raum (...) '. – jfs

+0

Es könnte nützlich sein zu erwähnen, welche Versionen von Python Sie verwenden. Ich denke nicht, dass dies ein Problem für eine Version von Python 3 ist (ich denke 3.5, aber nicht 3.4). –

Antwort

18

Keine Notwendigkeit Croom

Sie nicht verwenden CRoom in person.py, also nicht importieren zu importieren. Aufgrund der dynamischen Bindung muss Python nicht "alle Klassendefinitionen zur Kompilierzeit sehen".

Wenn Sie tatsächlich tun Verwendung CRoom in person.py, dann from room import CRoom-import room und room.CRoom Modul qualifizierten Formular ändern. Einzelheiten finden Sie unter Effbot's Circular Imports.

Hinweis: haben Sie wahrscheinlich einen Fehler in Self.NextId += 1 Zeile. Es erhöht 10 der Instanz, nicht 10 der Klasse. Um den Zähler der Klasse zu erhöhen, verwenden Sie CRoom.NextId += 1 oder Self.__class__.NextId += 1.

+0

Also wie soll die Person sehen, wer noch im Raum ist, oder die Tür etc. finden, wenn sie keinen Bezug zum Raum haben, in dem sie sich befinden? –

+0

Entfernen Sie einfach "aus dem Raum importieren CRoom". Sie können weiterhin CRoom-Instanzen von außen an person.py übergeben. Sie können den CRoom-Konstruktor nicht von person.py aufrufen, aber Sie tun es trotzdem nicht. – Constantin

+0

Der zweite Vorschlag kann nicht funktionieren. Du importierst das Modul und nicht die Klasse, also bekommst du 'AttributError: 'Modul' Objekt hat kein Attribut '. –

7

Haben Sie eigentlich die Klassen in Klassendefinition Zeit verweisen müssen? dh.

Oder (wahrscheinlicher), müssen Sie nur CPerson in den Methoden Ihrer Klasse verwenden (und umgekehrt). zB:

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

Wenn die zweite, dann ist es kein Problem - wie sie in der Zeit der Methode eher aufgerufen wird als definiert, wird das Modul importiert werden. Ihr einziges Problem ist, wie man sich darauf bezieht. Wahrscheinlich tun Sie so etwas wie:

from CRoom import CPerson # or even import * 

mit zirkular Referenzierung Modulen können Sie dies nicht tun, wie an dem Punkt ein Modul importiert eine andere, wird die ursprüngliche Module Körper nicht die Ausführung beendet haben, so der Namensraum wird unvollständig sein. Verwenden Sie stattdessen qualifizierte Referenzen. dh:

#croom.py 
import cperson 
class CRoom(object): 
    def getPerson(self): return cperson.CPerson("someone") 

Hier Python muss nicht das Attribut zum Nachschlagen auf dem Namespace, bis die Methode tatsächlich aufgerufen wird, in welcher Zeit beide Module ihre Initialisierung abgeschlossen haben sollten.

1

Sie könnten nur alias die zweite.

import CRoom 

CPerson = CRoom.CPerson 
+0

Das wird nicht funktionieren, als wäre es im Modul topleve, CRoom.CPerson möglicherweise noch nicht vorhanden. Der einzige Weg, wie Sie es tun könnten, wäre, Ihren Namen in den anderen Modul-Namespace des anderen Moduls zu stecken (dh import croom; croom.CPerson = CPerson), was sehr hacky ist. Besser, voll qualifizierte Namen zu verwenden. – Brian

2

Erstens ist die Benennung Ihrer Argumente mit Großbuchstaben verwirrend. Da Python keine formale, statische Typprüfung hat, verwenden wir UpperCase als eine Klasse und lowerCase als Argument.

Zweitens kümmern wir uns nicht mit CRoom und CPerson. Großbuchstaben reichen aus, um anzuzeigen, dass es sich um eine Klasse handelt. Der Buchstabe C wird nicht verwendet. Room. Person.

Drittens legen wir normalerweise keine Dinge in Eine Klasse pro Datei Format. Eine Datei ist ein Python-Modul, und wir importieren häufiger ein ganzes Modul mit allen Klassen und Funktionen.

[Ich bin mir bewusst, das sind Gewohnheiten - Sie brauchen sie nicht heute zu brechen, aber sie machen es schwer zu lesen.]

Python nicht statisch wie C definierte Typen nicht verwendet ++. Wenn Sie eine Methodenfunktion definieren, definieren Sie den Datentyp der Argumente für diese Funktion nicht formal. Sie listen nur einige Variablennamen auf. Hoffentlich liefert die Clientklasse Argumente vom richtigen Typ.

Wenn Sie zur Laufzeit eine Methodenanforderung erstellen, muss Python sicher sein, dass das Objekt über die Methode verfügt. HINWEIS. Python überprüft nicht, ob das Objekt der richtige Typ ist - das spielt keine Rolle. Es prüft nur, ob es die richtige Methode hat.

Die Schleife zwischen room.Room und person.Person ist ein Problem. Sie müssen keine hinzufügen, wenn Sie die andere definieren.

Am sichersten ist es, das gesamte Modul zu importieren.

Hier 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 

Arbeiten in Ordnung, solange Person schließlich im Namensraum definiert ist, wo diese ausgeführt werden. Die Person muss nicht bekannt sein, wenn Sie die Klasse definieren.

Die Person muss erst zur Laufzeit bekannt sein, wenn dann der Ausdruck Person (...) ausgewertet wird.

Hier person.py

import room 
class Person(object): 
    def something(self, x, y): 
     aRoom= room.Room() 
     aRoom.addPerson(self.firstName, self.lastName, self.gender) 

Ihre main.py wie dieses

import room 
import person 
r = room.Room(...) 
r.addPerson("some", "name", "M") 
print r 
0

@ S.Lott , wenn ich irgendetwas in das Raummodul nicht importieren sieht ich stattdessen einen undefinierten Fehler (ich importiert es in das Hauptmodul wie Sie gezeigt haben)

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

Auch der Grund dort verschiedene Module ist, wo ich das Problem zu Beginn mit der Container-Klasse (zB der Raum) ist schon mehrere hundert Zeilen, also wollte ich die Elemente in ihm (z. B. die Leute) in einer separaten Datei.

EDIT: 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() 
+0

Die Hauptursache des Fehlers ist in main. Wie sieht das Haupt aus? Wird Raum und Person importiert? –

+0

main.py zur Bearbeitung hinzugefügt. –

+0

Danke. Mein Fehler im Bereich Regeln. Jedes Modul hat seinen eigenen Namensraum. Der Raum wird im Namensraum des Raummoduls ausgeführt. –