2009-06-25 10 views
6

Tengo dos clases que se refieren entre sí, pero obviamente el compilador se queja. ¿Hay alguna forma de evitar esto?clases de python que se refieren entre sí

EDITAR

En realidad, mi código es ligeramente diferente a la que utiliza Hank Gay. Entonces, python definitivamente puede tratar con algunos tipos de referencias circulares, pero arroja un error en la siguiente situación. Debajo está lo que tengo y me sale un 'nombre Y no se define el error'

class X(models.Model): 

     creator = Registry() 
     creator.register(Y) 

class Y(models.Model): 
    a = models.ForeignKey(X) 
    b = models.CharField(max_length=200) 

Espero que esto ayude a aclarar. Alguna sugerencia.

+4

¿Qué es "obvio"? ¿De dónde se queja? Mostrar código? Las referencias circulares no son un problema para Python, el problema está en otra parte. –

+0

¿Cómo se ve el código? –

+0

wow ... * rolleyes * – Robbie

Respuesta

15

En python, el código de una clase se ejecuta cuando se carga la clase.

Ahora, ¿qué diablos significa eso? ;-)

Considere el siguiente código:

class x: 
    print "hello" 
    def __init__(self): print "hello again" 

Cuando se carga el módulo que contiene el código, pitón imprimirá hello. Cada vez que cree un x, python imprimirá hello again.

Puede pensar en def __init__(self): ... como equivalente con __init__ = lambda self: ..., excepto que no se aplican ninguna de las restricciones de python lambda. Es decir, def es una tarea, lo que podría explicar por qué se ejecuta código fuera de métodos pero no dentro de métodos.

Cuando el código dice

class X(models.Model): 
    creator = Registry() 
    creator.register(Y) 

referirlo a Y cuando se carga el módulo, antes de Y tiene un valor. Se puede pensar en class X como una asignación (pero no puede recordar la sintaxis para crear clases anónimas fuera de la mano, tal vez es una invocación de type?)

Lo que es posible que desee hacer es esto:

class X(models.Model): 
    pass 
class Y(models.Model): 
    foo = something_that_uses_(X) 
X.bar = something_which_uses(Y) 

Es decir, cree los atributos de clase de X que se crea Y después de Y. O viceversa: cree Y primero, luego X, luego los atributos de Y que dependen de X, si es más fácil.

Espero que esto ayude :)

2

ACTUALIZACIÓN: Cambió la pregunta después de mi respuesta. La solución actualmente aceptada es mejor a la luz de la nueva pregunta.

¿Qué estás diciendo que es el problema?

class A(object): 
    def __init__(self): 
     super(A, self).__init__() 


    def b(self): 
     return B() 


class B(object): 
    def __init__(self): 
     super(B, self).__init__() 


    def a(self): 
     return A() 

Esto compila y funciona perfectamente.

+0

Alguna información adicional: Esto funciona, sí. Pero tiene un precio: solo funciona si coloca todas las clases en un solo archivo. Esto es bastante inconveniente: podría ser muy pitónico, no sé, pero sé que se rompe con la buena práctica de OOP de usar un archivo propio para cada clase (pública). –

2

Siempre que trabaje dentro de un método, puede acceder al objeto de la clase.

Por lo tanto, el ejemplo anterior no tiene problemas si se mueve creator.register(Y) dentro de __init__. Sin embargo, no puede tener referencias circulares a clases fuera de los métodos.

+1

Esta respuesta no explica nada. Da una "solución alternativa" sin comprender o explicar el problema real, que se explica en las respuestas de Jonas y John Machin. –

2

El error es que la ejecución de creator.register(Y) se trató durante la (ejecutable) definición de la clase X, y en esa etapa, la clase Y no está definido. Comprenda esto: class y def son sentencias que se ejecutan (generalmente en el momento de la importación); no son "declaraciones".

Sugerencia: cuéntenos qué está tratando de lograr, tal vez como una nueva pregunta.

-2

El problema probablemente no es Python. Yo pensaría que es un problema de SQL. Las clases son a través de una capa de abstracción convertida a una consulta SQL para crear una tabla. Está intentando hacer referencia de una tabla a otra que en ese momento aún no existe.

En SQL se resolvería mediante la creación de la tabla por primera vez sin las referencias y después de que modificarlos para hacer esas referencias,

Sin embargo no estoy seguro de mi respuesta, por lo que tomar con mucha sazón, yo en realidad estaría bastante sorprendido si la capa de abstracción de la base de datos de Django no trata bien las referencias cruzadas.

Cuestiones relacionadas