2009-02-07 9 views
22

Ho una serie di classi Python in un file. Alcune classi fanno riferimento ad altre.Python ha prototipi di classe (o dichiarazioni di inoltro)?

Il mio codice è qualcosa di simile:

class A(): 
    pass 

class B(): 
    c = C() 

class C(): 
    pass 

Cercando di correre che, ottengo NameError: name 'C' is not defined. Giusto, ma c'è un modo per farlo funzionare, o devo riordinare manualmente le mie classi per sistemare? In C++, posso creare un prototipo di classe. Python ha un equivalente?

(In realtà sto giocando con i modelli Django, ma ho cercato di non complicare le cose).

+0

FWIW, è chiamato http://en.wikipedia.org/wiki/Forward_declaration, non prototype (http://en.wikipedia.org/wiki/Prototype-based_programming). – Constantin

+0

Si chiama prototipo di funzione in Kernighan e Ritchie, da dove lo ricordo. – Mat

+0

Appena controllato, nessun "prototipo di classe" nella mia copia K & R;) – Constantin

risposta

31

In Python non si crea un prototipo di per sé, ma è necessario comprendere la differenza tra "attributi di classe" e attributi a livello di istanza. Nell'esempio che hai mostrato sopra, stai dichiarando un attributo di classe sulla classe B, non un attributo a livello di istanza.

Questo è quello che state cercando:

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

Sarei curioso di una spiegazione sul perché Python possa trovare il def'n di C quando assegna un attributo di istanza ma non uno di livello di classe. È perché sta provando a fare il compito alla definizione della classe piuttosto che al runtime? – Dana

+1

Sì, poiché c = C() è nella definizione di classe (eseguita su carico del modulo), la classe C non esiste ancora. – truppo

+1

@truppo è corretto. Quando si dichiarano gli attributi di classe i riferimenti corrispondenti vengono risolti quando il modulo viene caricato (vale a dire che la classe è interpretata). Il metodo __init__ è analogo ai costruttori in altri linguaggi, quindi i riferimenti nel suo ambito locale non devono essere risolti fino a quando le variabili di livello di classe –

6

Questo risolverebbe il problema come presentato (ma penso che cercate per un attributo esempio come jholloway7 risposto):

class A: 
    pass 

class B: 
    pass 

class C: 
    pass 

B.c = C() 
+1

Nel caso dei modelli Django, questa è l'unica risposta che funziona poiché stiamo cercando di modificare la classe stessa, non le istanze della classe. Django introspe le classi e usa i metadati per guidare il suo livello ORM. – verveguy

+0

Ritraggo il mio commento: questo è un buon modo per aggiungere membri di classe a una classe Python senza bisogno di forward decl. Ma * non * in realtà risolve il problema delle dichiarazioni del modello Django a causa di qualcosa (?) Interno al modo in cui Django elabora questi modelli. – verveguy

0

Tutte le risposte corrette sugli attributi di classe vs istanza. Tuttavia, il motivo per cui hai un errore è solo l'ordine di definire le tue classi. Ovviamente la classe C non è ancora stata definita (poiché il codice a livello di classe viene eseguito immediatamente all'importazione):

class A(): 
    pass 

class C(): 
    pass 

class B(): 
    c = C() 

Funzionerà.

+1

So che posso farlo per risolverlo (ed è quello che ho fatto per ora) ma ora il mio codice di ordinazione non è intuitivo. – Mat

+0

Bene, capisco cosa intendi, ma penso che sarebbe più non intuitivo cercare di usare qualcosa che non esiste ancora. Come fare: stampare a; a = 5 –

+1

Cercando di eseguire alcune istruzioni come stampare a; a = 5 chiaramente non ha molto senso, ma una classe è autonoma e perfettamente ragionevole per il futuro riferimento se i prototipi di classe C fossero disponibili. – Mat

3

Python non ha prototipi o classi aperte in stile Ruby. Ma se davvero ne hai bisogno, puoi scrivere un metaclasse che sovraccarichi nuovo in modo che esegua una ricerca nello spazio dei nomi corrente per vedere se la classe esiste già e se restituisce l'oggetto di tipo esistente anziché crearne uno nuovo . Ho fatto qualcosa di simile su un ORM che ho scritto qualche tempo fa e ha funzionato molto bene.

+0

la risposta è no, ma è strano, potresti esemplificare la tua soluzione? quando a dipende da b, b dipende da c e c dipende da a. –

40

In realtà, tutte le osservazioni sopra riportate sono ottime su Python, ma nessuna di esse risolverà il problema.

Django ha bisogno di introspettarsi.

Il destra modo per fare ciò che si vuole è la seguente:

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

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

Si noti l'uso del nome della classe come una stringa piuttosto che il riferimento di classe letterale. Django offre questa alternativa per affrontare esattamente il problema che Python non fornisce dichiarazioni anticipate.

Questa domanda mi ricorda la classica domanda di supporto che dovresti sempre porre a qualsiasi cliente con un problema: "Cosa stai provando a fare ?"

+1

Era chiaro dalla domanda che cosa l'OP stava cercando di fare, che era relativo a Django e che l'OP stava cercando di capire il modello di Python per la programmazione orientata agli oggetti, perché l'ORM di Django può confondere i principianti. –

+0

Potrebbe essere vero, ma quello che l'OP ha finito per chiedere era molto diverso dal titolo della domanda. Sono riluttante a cambiare il titolo tre anni dopo, anche se è impreciso. – ObscureRobot

+0

Grazie! Questo è il problema esatto in cui stavo entrando. – Lander

Problemi correlati