Dipendenza dal modulo Python
-
03-07-2019 - |
Domanda
Ok ho due moduli, ognuno contenente una classe, il problema è che le loro classi si fanno riferimento l'un l'altro.
Diciamo ad esempio che avevo un modulo room e un modulo persona contenenti CRoom e CPerson.
La classe CRoom contiene informazioni sulla stanza e un elenco CPerson di ognuno nella stanza.
La classe CPerson tuttavia a volte ha bisogno di usare la classe CRoom per la stanza in cui si trova, ad esempio per trovare la porta o anche vedere chi altro è nella stanza.
Il problema è che i due moduli si importano a vicenda. Ho appena ricevuto un errore di importazione sul quale viene mai importato secondo :(
In c ++ ho potuto risolvere questo problema includendo solo le intestazioni e poiché in entrambi i casi le classi hanno solo puntatori all'altra classe, una dichiarazione in avanti sarebbe sufficiente per l'intestazione, ad esempio:
class CPerson;//forward declare
class CRoom
{
std::set<CPerson*> People;
...
Esiste un modo per farlo in Python, oltre a mettere entrambe le classi nello stesso modulo o qualcosa del genere?
modifica: aggiunto esempio di pitone che mostra un problema usando le classi precedenti
errore:
Traceback (ultima chiamata più recente):
File " C: \ Projects \ python \ test \ main.py " ;, riga 1, in
da room import CRoom
File " C: \ Projects \ python \ test \ room.py " ;, riga 1, in
da persona importazione CPerson
File " C: \ Projects \ python \ test \ person.py " ;, riga 1, in
da room import CRoom
ImportError: impossibile importare il nome 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)
Soluzione
Non è necessario importare CRoom
Non usi CRoom
in person.py
, quindi non importarlo. A causa dell'associazione dinamica, Python non ha bisogno di " vedere tutte le definizioni di classe al momento della compilazione " ;.
Se in realtà fai usi CRoom
in person.py
, quindi cambia da CRoom di importazione delle stanze
in < codice> import room e utilizza il modulo qualificato room.CRoom
. Vedi Importazioni circolari di Effbot per dettagli.
Sidenote: probabilmente hai un errore nella riga Self.NextId + = 1
. Incrementa NextId
dell'istanza, non NextId
della classe. Per incrementare il contatore della classe usa CRoom.NextId + = 1
o Self .__ class __. NextId + = 1
.
Altri suggerimenti
In realtà devi fare riferimento alle classi al momento della definizione della classe? vale a dire.
class CRoom(object):
person = CPerson("a person")
O (più probabilmente), devi solo usare CPerson nei metodi della tua classe (e viceversa). ad esempio:
class CRoom(object):
def getPerson(self): return CPerson("someone")
Se il secondo non presenta alcun problema, poiché quando il metodo viene chiamato anziché definito, il modulo verrà importato. Il tuo unico problema è come fare riferimento ad esso. Probabilmente stai facendo qualcosa del tipo:
from CRoom import CPerson # or even import *
Con i moduli a riferimento circolare, non puoi farlo, poiché nel momento in cui un modulo ne importa un altro, il corpo dei moduli originali non avrà terminato l'esecuzione, quindi lo spazio dei nomi sarà incompleto. Utilizzare invece riferimenti qualificati. vale a dire:
#croom.py
import cperson
class CRoom(object):
def getPerson(self): return cperson.CPerson("someone")
Qui, python non ha bisogno di cercare l'attributo nello spazio dei nomi fino a quando il metodo non viene effettivamente chiamato, quando entrambi i moduli dovrebbero aver completato la loro inizializzazione.
Innanzitutto, dare un nome ai tuoi argomenti con lettere maiuscole è fonte di confusione. Poiché Python non ha un controllo formale e statico del tipo, usiamo UpperCase
per indicare una classe e lowerCase
per indicare un argomento.
In secondo luogo, non ci preoccupiamo di CRoom e CPerson. Le maiuscole sono sufficienti per indicare che è una classe. La lettera C non è usata. Camera
. persona
.
Terzo, di solito non inseriamo le cose nel formato Una classe per file . Un file è un modulo Python e più spesso importiamo un intero modulo con tutte le classi e funzioni.
[Sono consapevole che quelle sono abitudini - non è necessario romperle oggi, ma rendono difficile la lettura.]
Python non usa tipi definiti staticamente come C ++. Quando si definisce una funzione del metodo, non si definisce formalmente il tipo di dati degli argomenti per quella funzione. Elencate semplicemente alcuni nomi di variabili. Eventualmente, la classe client fornirà argomenti del tipo corretto.
In fase di esecuzione, quando si effettua una richiesta di metodo, Python deve assicurarsi che l'oggetto abbia il metodo. NOTA. Python non controlla se l'oggetto è del tipo giusto, non importa. Controlla solo se ha il metodo giusto.
Il loop tra room.Room
e person.Person
è un problema. Non è necessario includerne uno quando si definisce l'altro.
È più sicuro importare l'intero modulo.
Ecco 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
Funziona bene fintanto che Person viene definito nello spazio dei nomi in cui è in esecuzione. La persona non deve essere conosciuta quando si definisce la classe.
La persona non deve essere conosciuta fino al runtime quando viene valutata l'espressione Persona (...).
Ecco person.py
import room
class Person( object ):
def something( self, x, y ):
aRoom= room.Room( )
aRoom.addPerson( self.firstName, self.lastName, self.gender )
Il tuo main.py
è simile a questo
import room
import person
r = room.Room( ... )
r.addPerson( "some", "name", "M" )
print r
Potresti semplicemente alias il secondo.
import CRoom
CPerson = CRoom.CPerson
@ S. Lott se non importare nulla nel modulo della stanza, invece, viene visualizzato un errore indefinito (l'ho importato nel modulo principale come mostrato)
Traceback (ultima chiamata più recente):
File " C: \ Projects \ python \ test \ main.py " ;, riga 6, in
Ben = Room.AddPerson ('Ben', 'Blacker', 'Male')
File " C: \ Projects \ python \ test \ room.py " ;, riga 12, in AddPerson
Person = CPerson (FirstName, SecondName, Gender, Id)
NameError: il nome globale "CPerson" non è definito
Inoltre, la ragione per cui i diversi moduli è dove ho riscontrato il problema per iniziare con la classe container (ieg the room) sono già diverse centinaia di righe, quindi volevo che gli elementi in essa contenuti (ad esempio le persone) in un file separato.
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()