2008-10-01 15 views
16

Ok Mam dwa moduły, z których każdy zawiera klasę, problem polega na tym, że ich klasy odwołują się nawzajem.Zależność od modułu Pythona

Powiedzmy na przykład, że miałem moduł pokojowy i moduł osoby zawierający CRoom i CPerson.

Klasa CRoom zawiera informacje o pokoju i listę CPerson każdego z pokoi.

Klasy CPerson jednak czasami trzeba użyć klasy CRoom dla pokoju, w którym się znajduje, na przykład, aby znaleźć drzwi, lub też zobaczyć, kto jeszcze jest w pokoju.

Problem jest z dwóch modułów importu siebie po prostu pojawia się błąd importu, na których kiedykolwiek jest importowany sekund :(

w C++ mogę rozwiązać ten problem poprzez włączenie tylko nagłówki, a ponieważ w obu przypadkach zajęcia prostu odnośniki do drugiej klasy, do przodu deklaracja wystarczy dla nagłówka np:

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

Czy mimo to zrobić w Pythonie, inne niż umieszczenie obu klas w tym samym module czy coś takiego?

edit: dodaje python przykład pokazujący problem, stosując powyższe zajęcia

błąd:

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

można dodawać niewielką przypadek testowy, który odtwarza swój błąd? Próbowałem stworzyć dwa moduły, które odwołują się do siebie i nie mają problemów, więc zakładam, że jest jakiś subtelny punkt, którego mi brakuje. –

+2

[offtop] Przeczytaj przewodnik w stylu Pythona http://www.python.org/dev/peps/pep-0008/. W szczególności upuść pierwsze "C" od nazw klas, wszystkie inne nazwy w twoim przykładzie powinny być pisane małymi literami. Aby odpowiedzieć na twoje pytanie: po prostu "zaimportuj pomieszczenie", a w metodach osoby użyj "room.Room (...)". – jfs

+0

może być przydatne wspomnieć, których wersji python używasz. Nie sądzę, jest to problem dla niektórych wersji Pythona 3 (myślę, że 3.5, ale nie 3.4). –

Odpowiedz

18

Nie trzeba importować Croom

Nie używać CRoom w person.py, więc nie go importować. Ze względu na dynamiczne wiązanie Python nie musi "widzieć wszystkich definicji klas podczas kompilacji".

Jeśli rzeczywiście zrobić użycie CRoom w person.py, a następnie zmienić from room import CRoom do import room i użyć modułu kwalifikowana postać room.CRoom. Szczegóły: Effbot's Circular Imports.

Sidenote: Prawdopodobnie masz błąd w linii Self.NextId += 1. Liczba ta wzrasta o NextId, a nie o NextId klasy. Aby zwiększyć liczbę kont przeciwdziałających klasie: CRoom.NextId += 1 lub Self.__class__.NextId += 1.

+0

Więc w jaki sposób osoba ma zobaczyć, kto jeszcze jest w pokoju, lub znaleźć drzwi itp., Jeśli nie mają one refrenacji do ich pokoju? –

+0

Po prostu usuń "z pokoju import CRoom". Nadal będzie można przekazywać instancje CRoom do pliku person.py z zewnątrz. Nie będziesz mógł wywołać konstruktora CRoom z pliku person.py, ale i tak tego nie zrobisz. – Constantin

+0

Druga propozycja nie może działać. Zaimportujesz moduł, a nie klasę, więc otrzymasz obiekt "AttributeError:" moduł "nie ma atrybutu". –

7

Czy rzeczywiście trzeba odwołać zajęcia w momencie definicji klasy? to znaczy.

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

Czy (bardziej prawdopodobne), czy po prostu trzeba użyć CPerson w metodach swojej klasy (i na odwrót). np:

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

Jeśli drugi, to nie ma problemu - jak przez czas sposób ją nazwie niż zdefiniowany moduł zostaną zaimportowane. Twoim jedynym problemem jest, jak się do tego odnosić. Prawdopodobnie robisz coś takiego:

from CRoom import CPerson # or even import * 

Z kołowo modułów odsyłania, że ​​nie może tego zrobić, ponieważ w punkcie jednego modułu importu inny, oryginalny moduły organizm nie zakończeniu wykonywania, więc nazw będzie niekompletne. Zamiast tego użyj kwalifikowanych referencji. tj:

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

Tutaj python nie trzeba odnośnika atrybut przestrzeni nazw do metody rzeczywiście jest wywoływana, w którym to czasie oba moduły powinny zakończyły inicjalizacji.

1

Można tylko pseudonim drugi.

import CRoom 

CPerson = CRoom.CPerson 
+0

To nie zadziała, tak jak w module topleve, CRoom.CPerson może jeszcze nie istnieć. Jedynym sposobem, by to zrobić, byłoby wsunięcie twojego nazwiska do przestrzeni nazw innych modułów z drugiego modułu (np. Import croom, croom.CPerson = CPerson), który jest bardzo hacky. Lepiej używać w pełni kwalifikowanych nazw. – Brian

2

Po pierwsze, nazywanie argumentów dużymi literami jest mylące. Ponieważ Python nie ma formalnego, statycznego sprawdzania typów, używamy UpperCase do oznaczania klasy i lowerCase do oznaczania argumentu.

Po drugie, nie przejmujemy się CRoom i CPerson. Wielkie litery są wystarczające, aby wskazać, że jest to klasa. Litera C nie jest używana. Room. Person.

Po trzecie, zazwyczaj nie umieszczamy elementów w formacie Jedna klasa na plik. Plik jest modułem Pythona i coraz częściej importujemy cały moduł ze wszystkimi klasami i funkcjami.

[Jestem świadomy to są nawyki - nie trzeba łamać je dzisiaj, ale nie sprawiają, że trudno odczytać.]

Python nie używa statycznie zdefiniowanych typów jak C++. Po zdefiniowaniu funkcji metody nie definiuje się formalnie typu danych argumentów tej funkcji. Po prostu podajesz nazwy zmiennych. Mamy nadzieję, że klasa klienta dostarczy argumenty prawidłowego typu.

W czasie wykonywania, po wywołaniu metody, Python musi upewnić się, że obiekt ma tę metodę. UWAGA. Python nie sprawdza, czy obiekt jest prawidłowy - to nie ma znaczenia. Sprawdza tylko, czy ma właściwą metodę.

Pętla między room.Room i person.Person jest problemem. Nie musisz uwzględniać jednego przy definiowaniu drugiego.

Najbezpieczniej jest importować cały moduł.

Oto 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 

działa dobrze tak długo, jak osoba jest ostatecznie zdefiniowane w przestrzeni nazw, jeżeli jest to wykonującego. Osoba nie musi być znana, kiedy definiujesz klasę.

Osoba nie musi być znana do czasu wykonania, gdy następnie oceniane jest wyrażenie Person (...).

Oto person.py

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

Twój main.py wygląda to

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

@ S.Lott gdybym nic nie importować do modułu pokojowej pojawia się niezdefiniowany błąd zamiast (I importowany do głównego modułu, jak pokazano)

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

Powodem, dla którego różne moduły są tam, gdzie napotkany problem zaczynał się od klasy kontenera (np. Pokoju), jest już kilkaset linii, więc chciałem, aby znajdujące się w nim elementy (np. Ludzie) znajdowały się w osobnym pliku.

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

Podstawową przyczyną błędu jest główna. Jak wygląda główny? Czy importuje pokój i osobę? –

+0

Dodano plik main.py do edycji. –

+0

Dzięki. Mój błąd dotyczący zasad zasięgu. Każdy moduł ma swoją własną przestrzeń nazw. Pokój jest uruchamiany w przestrzeni nazw modułu pokoju. –