2009-02-07 10 views
22

Tengo una serie de clases de Python en un archivo. Algunas clases hacen referencia a otras.¿Python tiene prototipos de clase (o declaraciones avanzadas)?

Mi código es algo como esto:

class A(): 
    pass 

class B(): 
    c = C() 

class C(): 
    pass 

Tratando de correr que, consigo NameError: name 'C' is not defined. Bastante, pero ¿hay alguna manera de hacerlo funcionar, o tengo que volver a ordenar manualmente mis clases para acomodarlas? En C++, puedo crear un prototipo de clase. ¿Python tiene un equivalente?

(En realidad estoy jugando con los modelos de Django, pero traté de no complicar las cosas).

+0

FWIW, se llama http://en.wikipedia.org/wiki/Forward_declaration, no como prototipo (http://en.wikipedia.org/wiki/Prototype-based_programming). – Constantin

+0

Se llama prototipo de función en Kernighan y Ritchie, de donde yo la recuerdo. – Mat

+0

Acabo de marcar, no hay "prototipos de clase" en mi copia de K & R;) – Constantin

Respuesta

31

En Python no se crea un prototipo per se, pero sí se debe entender la diferencia entre los "atributos de clase" y los atributos de nivel de instancia. En el ejemplo que ha mostrado arriba, está declarando un atributo de clase en la clase B, no un atributo de nivel de instancia.

Esto es lo que busca:

class B(): 
    def __init__(self): 
     self.c = C() 
+1

Me gustaría tener una explicación de por qué Python puede encontrar la definición de C cuando está asignando un atributo de instancia pero no un nivel de clase. ¿Es porque está tratando de hacer la tarea en la definición de clase en lugar de hacerlo en tiempo de ejecución? – Dana

+1

Sí, dado que c = C() está en la definición de clase (se ejecuta al cargar el módulo), la clase C aún no existe. – truppo

+1

@truppo es correcto. Cuando declara los atributos de clase, las referencias correspondientes se resuelven cuando se carga el módulo (es decir, se interpreta la clase). El método __init__ es análogo a los constructores en otros idiomas, por lo que las referencias en su ámbito local no tienen que resolverse hasta que se invoquen –

6

Esto resolvería el problema que se presenta (pero creo que usted está realmente en busca de un atributo de instancia como jholloway7 respondió):

class A: 
    pass 

class B: 
    pass 

class C: 
    pass 

B.c = C() 
+1

En el caso de los modelos de Django, esta es la única respuesta que funciona ya que estamos tratando de modificar la clase en sí misma, no las instancias de la clase. Django introspecta las clases y usa esos metadatos para conducir su capa ORM. – verveguy

+0

Me retracto de mi comentario: esto está bien como una manera de agregar miembros de la clase a una clase de Python sin necesidad de crear decls. Pero * no * en realidad resuelve el problema para las declaraciones del modelo Django debido a algo (?) Interno a la forma en que Django procesa estos modelos. – verveguy

0

Todas las respuestas correctas sobre clase vs atributos de instancia. Sin embargo, la razón por la que tiene un error es solo el orden de definir sus clases. Por supuesto, la clase C aún no se ha definido (ya que el código de nivel de clase se ejecuta inmediatamente en la importación):

class A(): 
    pass 

class C(): 
    pass 

class B(): 
    c = C() 

Funcionará.

+1

Sé que puedo hacer eso para solucionarlo (y es lo que he hecho por ahora) pero ahora mi orden de código no es intuitiva. – Mat

+0

Bueno, veo lo que quieres decir, pero creo que sería más no intuitivo intentar usar algo que aún no existe. Me gusta hacer: imprimir a; a = 5 –

+1

Intentando ejecutar algunas afirmaciones como imprimir a; a = 5 claramente no tiene mucho sentido, pero una clase es autónoma y perfectamente razonable para reenviar referencias si los prototipos de clase estilo C estaban disponibles. – Mat

3

Python no tiene prototipos ni clases abiertas de estilo Ruby. Pero si realmente los necesita, puede escribir una metaclase que sobrecargue new para que haga una búsqueda en el espacio de nombres actual para ver si la clase ya existe y si devuelve el objeto de tipo existente en lugar de crear uno nuevo . Hice algo así en un ORM que escribí hace un tiempo y funcionó muy bien.

+0

la respuesta es no, pero es extraño, ¿podría ejemplificar su solución? cuando a depende de b, b depende de c y c depende de a. –

40

En realidad, todas las anteriores son excelentes observaciones sobre Python, pero ninguna de ellas resolverá su problema.

Django necesita introspección.

El derecho manera de hacer lo que quiere es la siguiente:

class Car(models.Model): 
    manufacturer = models.ForeignKey('Manufacturer') 
    # ... 

class Manufacturer(models.Model): 
    # ... 

Nota el uso del nombre de la clase como una cadena en lugar de la referencia de clase literal. Django ofrece esta alternativa para lidiar exactamente con el problema de que Python no proporciona declaraciones directas.

Esta pregunta me recuerda a la clásica pregunta de soporte técnico que siempre debe formularse a cualquier cliente con un problema: "¿Qué está tratando de hacer realmente realmente?"

+1

Estaba claro por la pregunta qué intentaba hacer el OP, que estaba relacionado con Django y que el OP intentaba entender el modelo de Python para la programación orientada a objetos, porque el ORM de Django puede ser confuso para los principiantes. –

+0

Eso puede ser cierto, pero lo que el OP terminó preguntando fue muy diferente del título de la pregunta. Soy reacio a cambiar el título tres años después, a pesar de que es inexacto. – ObscureRobot

+0

¡Gracias! Este es el problema exacto en el que me estaba metiendo. – Lander

Cuestiones relacionadas