Question

Ok, j'ai deux modules, chacun contenant une classe, le problème est que leurs classes se référencent.

Disons par exemple que j'avais un module de pièce et un module de personne contenant CRoom et CPerson.

La classe CRoom contient des informations sur la salle et une liste de tous les utilisateurs de la salle.

La classe CPerson doit toutefois parfois utiliser la classe CRoom pour la pièce dans laquelle elle se trouve, par exemple pour trouver la porte, ou pour voir qui d'autre se trouve dans la pièce.

Le problème vient des deux modules qui s’importent. Je viens d’obtenir une erreur d’importation qui est importée en second lieu: (

En c ++, je pouvais résoudre ce problème en n'incluant que les en-têtes, et puisque dans les deux cas, les classes n'ont que des pointeurs sur l'autre classe, une déclaration forward suffirait pour l'en-tête, par exemple:

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

Est-il possible de faire cela en python, mis à part placer les deux classes dans le même module ou quelque chose du genre?

edit: ajout d'un exemple python montrant un problème d'utilisation des classes ci-dessus

erreur:

  

Traceback (dernier appel passé):
   Fichier "C: \ Projects \ python \ test \ main.py", ligne 1, à
     à partir de la salle d'importation CRoom
   Fichier "C: \ Projects \ python \ test \ room.py", ligne 1, à
     de personne import CPerson
   Fichier "C: \ Projects \ python \ test \ person.py", ligne 1, à
     à partir de la salle d'importation CRoom
  ImportError: impossible d'importer le nom 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]

personne.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)
Était-ce utile?

La solution

Pas besoin d'importer CRoom

Vous n'utilisez pas CRoom dans person.py , ne l'importez donc pas. En raison de la liaison dynamique, Python n'a pas besoin de "voir toutes les définitions de classe au moment de la compilation".

Si vous utilisez le code CRoom dans personne.py , remplacez depuis le code d'importation de la salle par < code> import room et utilisez le formulaire qualifié de module room.CRoom . Voir Importations circulaires d'Effbot pour plus de détails.

Note: vous avez probablement une erreur dans la ligne Self.NextId + = 1 . Il incrémente NextId de l'instance, pas NextId de la classe. Pour incrémenter le compteur de la classe, utilisez CRoom.NextId + = 1 ou Self .__ class __. NextId + = 1 .

Autres conseils

Avez-vous réellement besoin de référencer les classes au moment de la définition de classe? c'est à dire.

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

Ou (plus probablement), avez-vous simplement besoin d'utiliser CPerson dans les méthodes de votre classe (et inversement). par exemple:

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

S'il n'y a pas de problème dans le second cas, le module sera importé dès que la méthode sera appelée plutôt que définie. Votre seul problème est de savoir comment vous y référer. Vous faites probablement quelque chose comme:

from CRoom import CPerson # or even import *

Avec les modules référencés de manière circulaire, vous ne pouvez pas le faire, car au moment où un module en importe un autre, le corps du module d'origine n'a pas fini de s'exécuter, de sorte que l'espace de nom sera incomplet. Utilisez plutôt des références qualifiées. c'est-à-dire:

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

Ici, python n'a pas besoin de rechercher l'attribut dans l'espace de nom jusqu'à ce que la méthode soit réellement appelée. Les deux modules devraient alors avoir terminé leur initialisation.

Tout d’abord, il est difficile de nommer vos arguments avec des lettres majuscules. Puisque Python n’a pas de vérification formelle et statique de type, nous utilisons UpperCase pour désigner une classe et lowerCase pour désigner un argument.

Deuxièmement, nous ne nous soucions pas de CRoom et de CPerson. Les majuscules suffisent pour indiquer que c'est une classe. La lettre C n'est pas utilisée. salle . Personne .

Troisièmement, nous ne mettons généralement pas les choses au format Une classe par fichier . Un fichier est un module Python, et nous importons plus souvent un module entier avec toutes les classes et fonctions.

[Je sais que ce sont des habitudes - vous n'avez pas besoin de les rompre aujourd'hui, mais elles rendent la lecture difficile.]

Python n'utilise pas de types définis statiquement, comme C ++. Lorsque vous définissez une fonction de méthode, vous ne définissez pas formellement le type de données des arguments de cette fonction. Vous ne faites que lister quelques noms de variables. Espérons que la classe client fournira des arguments du type correct.

Au moment de l'exécution, lorsque vous faites une demande de méthode, Python doit être sûr que l'objet possède la méthode. REMARQUE. Python ne vérifie pas si l'objet est du bon type - cela n'a pas d'importance. Il vérifie seulement s'il a la bonne méthode.

La boucle entre room.Room et person.Person pose un problème. Vous n'avez pas besoin d'en inclure un pour définir l'autre.

Il est plus prudent d'importer l'intégralité du module.

Voici 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 

Fonctionne bien tant que la personne est finalement définie dans l’espace de noms où elle s’exécute. Personne ne doit être connu lorsque vous définissez la classe.

Personne ne doit être connu avant l'exécution lorsque son expression Person (...) est évaluée.

Voici personne.py

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

Votre main.py ressemble à ceci

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

Vous pouvez simplement aliaser le deuxième.

import CRoom

CPerson = CRoom.CPerson

@ S.Lott si je n’importe rien dans le module de salle, j’obtiens à la place une erreur non définie (je l’ai importée dans le module principal, comme vous l’avez montré)

  

Traceback (dernier appel passé):
   Fichier "C: \ Projects \ python \ test \ main.py", ligne 6, à
     Ben = Room.AddPerson ('Ben', 'Blacker', 'Male')
   Fichier "C: \ Projects \ python \ test \ room.py", ligne 12, dans AddPerson

     Person = CPerson (Prénom, SecondName, Sexe, Id)
  NameError: le nom global 'CPerson' n'est pas défini

De plus, la raison pour laquelle il existe différents modules, c’est que j’ai rencontré le problème de commencer par la classe conteneur (par exemple, la salle) compte déjà plusieurs centaines de lignes;

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()
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top