2008-10-01 33 views
16

Ok Tengo dos módulos, cada uno contiene una clase, el problema es que sus clases se referencian entre sí.Dependencia del módulo Python

Digamos, por ejemplo, que tenía un módulo de sala y un módulo de persona que contenía CRoom y CPerson.

La clase CRoom contiene información sobre la sala y una lista CPerson de cada uno en la sala.

La clase CPerson sin embargo a veces necesita usar la clase CRoom para la habitación, por ejemplo para encontrar la puerta, o también para ver quién más está en la habitación.

El problema es con los dos módulos de importación entre sí apenas consigo un error de importación en el que cada vez se está importando segundo :(

en C++ que podría resolver esto solamente incluyendo las cabeceras, y dado que en ambos casos las clases sólo tienen punteros a la otra clase, una declaración hacia adelante sería suficiente para la cabecera por ejemplo:

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

¿hay alguna forma de hacer esto en Python, con excepción de la colocación de ambas clases en el mismo módulo o algo por el estilo?

Edit: añadido ejemplo pitón muestra problema utilizando clases anteriores

error:

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

Se puede publicar un pequeño caso de prueba que reproduce el error? Traté de crear dos módulos que se refirieran entre sí y no tuvieran problemas, así que supongo que hay un punto sutil que me falta. –

+2

[offtop] Por favor, lea la guía de estilo de Python http://www.python.org/dev/peps/pep-0008/. Particularmente, suelte la primera "C" de los nombres de clase, todos los demás nombres en su ejemplo deberían estar en minúscula. Para responder a su pregunta: solo 'import room' y en los métodos de Person use 'room.Room (...)'. – jfs

+0

podría ser útil mencionar qué versiones de python estás usando. No creo que esto sea un problema para alguna versión de python 3 (creo que 3.5 pero no 3.4). –

Respuesta

18

No hay necesidad de importar CROOM

Usted no usa CRoom en person.py, así que no lo importa. Debido a la vinculación dinámica, Python no necesita "ver todas las definiciones de clase en tiempo de compilación".

Si realmente hace uso CRoom en person.py, a continuación, cambiar from room import CRoom-import room y utilizar la forma room.CRoom módulo cualificado. Ver Effbot's Circular Imports para más detalles.

Sidenote: es probable que tenga un error en la línea Self.NextId += 1. Incrementa NextId de instancia, no NextId de clase. Para incrementar el contador de la clase use CRoom.NextId += 1 o Self.__class__.NextId += 1.

+0

Entonces, ¿cómo se supone que la persona debe ver quién más está en la habitación, o encontrar la puerta, etc., si no tienen una referencia a la habitación en la que están? –

+0

Simplemente elimine "de la sala de importación CRoom". Aún podrá pasar instancias de CRoom a person.py desde afuera. No podrá llamar al constructor de CRoom desde person.py, pero no lo hará de todos modos. – Constantin

+0

La segunda proposición no puede funcionar. Usted importa el módulo y no la clase, por lo que obtendrá 'AttributeError: 'el objeto' module 'no tiene ningún atributo'. –

7

¿Es usted realmente necesita para hacer referencia a las clases en tiempo de definición de clase? es decir.

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

O (más probable), solo necesita usar CPerson en los métodos de su clase (y viceversa). por ejemplo:

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

Si el segundo, no hay problema - como en el momento en el método obtiene llamados en lugar de definir, se importará el módulo. Su único problema es cómo referirse a él. Es probable que estés haciendo algo como:

from CRoom import CPerson # or even import * 

Con los módulos de referenciación circularmente, no se puede hacer esto, como en el punto uno importaciones de módulos otra, el cuerpo original de módulos no habrán terminado de ejecutarse, por lo que el espacio de nombres estará incompleto En su lugar, use referencias calificadas. es decir:

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

Aquí, pitón no tiene que buscar el atributo en el espacio de nombres hasta que el método que realmente se llama, momento en el cual los dos módulos deben haber completado su inicialización.

1

Podría alias el segundo.

import CRoom 

CPerson = CRoom.CPerson 
+0

Eso no funcionará, como si se hubiera realizado en el módulo topleve, CRoom.CPerson puede no existir todavía. La única forma en que podría hacerlo sería insertar su nombre en el espacio de nombres de otros módulos del otro módulo (es decir, import croom; croom.CPerson = CPerson) que es muy hacky. Es mejor usar nombres completamente calificados. – Brian

2

Primero, nombrar sus argumentos con letras mayúsculas es confuso. Como Python no tiene una comprobación de tipo estática formal, usamos el UpperCase para referirnos a una clase y lowerCase para significar un argumento.

En segundo lugar, no nos molestamos con CRoom y CPerson. La mayúscula es suficiente para indicar que es una clase. La letra C no se usa. Room. Person.

En tercer lugar, no solemos poner cosas en el formato One Class Per File. Un archivo es un módulo de Python, y con mayor frecuencia importamos un módulo completo con todas las clases y funciones.

[Soy consciente de los hábitos son - no es necesario para romperlas hoy en día, pero, hacen que sea difícil de leer.]

Python no utiliza tipos definidos de forma estática como C++. Cuando define una función de método, no define formalmente el tipo de datos de los argumentos para esa función. Simplemente enumera algunos nombres de variables. Con suerte, la clase del cliente proporcionará argumentos del tipo correcto.

En tiempo de ejecución, cuando realiza una solicitud de método, Python debe asegurarse de que el objeto tenga el método. NOTA. Python no verifica si el objeto es del tipo correcto, eso no importa. Solo verifica si tiene el método correcto.

El bucle entre room.Room y person.Person es un problema. No necesita incluir uno al definir el otro.

Es más seguro importar todo el módulo.

Aquí es 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 

funciona bien, siempre y cuando la persona finalmente se define en el espacio de nombres donde esto se está ejecutando. La persona no tiene que ser conocida cuando define la clase.

No es necesario conocer la persona hasta el tiempo de ejecución cuando se evalúa la expresión Persona (...).

Aquí es person.py

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

Su main.py se parece a esto

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

@ S. Lott si no me importo nada en el módulo de habitación me da un error indefinido en vez (Importé en el módulo principal como se muestra)

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

Además, la razón por la que existen diferentes módulos es donde encontré el problema para comenzar con la clase contenedora (por ejemplo, la sala) ya son varios cientos de líneas, por lo que quería los elementos (por ejemplo, las personas) en un archivo separado.

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

La causa raíz del error está en main. ¿Cómo se ve el principal? ¿Importa Habitación y Persona? –

+0

Se agregó main.py en la edición. –

+0

Gracias. Mi error en las reglas de alcance. Cada módulo tiene su propio espacio de nombres. La sala se ejecuta en el espacio de nombres del módulo de sala. –

Cuestiones relacionadas