2009-11-19 23 views
97

Sto cercando di capire lo scope nelle classi annidate in Python. Qui è il mio codice di esempio:Ambito delle classi annidate?

class OuterClass: 
    outer_var = 1 
    class InnerClass: 
     inner_var = outer_var 

La creazione di classe non viene completata e ottengo l'errore:

<type 'exceptions.NameError'>: name 'outer_var' is not defined 

Cercando inner_var = Outerclass.outer_var non funziona. ottengo:

<type 'exceptions.NameError'>: name 'OuterClass' is not defined 

Sto cercando di accedere alla statica outer_var da InnerClass.

C'è un modo per farlo?

risposta

98
class Outer(object): 
    outer_var = 1 

    class Inner(object): 
     @property 
     def inner_var(self): 
      return Outer.outer_var 

Questo non del tutto è la stessa cose simili lavorano in altre lingue, e utilizza la ricerca globale, invece di scoping l'accesso alle outer_var. (Se si cambia quello oggetto il nome Outer è destinato a, allora questo codice utilizzare tale oggetto la prossima volta che viene eseguito.)

Se si desidera invece tutti Inner oggetti per avere un riferimento a un Outer perché outer_var è davvero attributo un'istanza:

class Outer(object): 
    def __init__(self): 
     self.outer_var = 1 

    def get_inner(self): 
     return self.Inner(self) 
     # "self.Inner" is because Inner is a class attribute of this class 
     # "Outer.Inner" would also work, or move Inner to global scope 
     # and then just use "Inner" 

    class Inner(object): 
     def __init__(self, outer): 
      self.outer = outer 

     @property 
     def inner_var(self): 
      return self.outer.outer_var 

noti che le classi estraibili è piuttosto raro in Python, e non implica automaticamente qualsiasi tipo di particolare rapporto tra le classi. Stai meglio non nidificare. (È comunque possibile impostare un attributo di classe su Outer su Inner, se lo si desidera.)

+1

Potrebbe essere utile aggiungere con quale versione (o versioni) di python la tua risposta funzionerà. –

+1

Ho scritto questo con 2.6/2.x in mente, ma, osservandolo, non vedo nulla che non funzionerebbe allo stesso modo in 3.x. –

+0

Non capisco cosa intendi in questa parte, "(Se cambi l'oggetto al quale è associato il nome esterno, allora questo codice utilizzerà quell'oggetto la prossima volta che verrà eseguito.)" Puoi aiutarmi a capire ? – batbrat

20

Potrebbe essere meglio se non si utilizzano classi annidate. Se si deve nido, provate questo:

x = 1 
class OuterClass: 
    outer_var = x 
    class InnerClass: 
     inner_var = x 

o dichiarare entrambe le classi prima di loro nidificazione: (. Dopo questo si può del InnerClass se necessario)

class OuterClass: 
    outer_var = 1 

class InnerClass: 
    inner_var = OuterClass.outer_var 

OuterClass.InnerClass = InnerClass 

40

penso che si può semplicemente fare:

class OuterClass: 
    outer_var = 1 

    class InnerClass: 
     pass 
    InnerClass.inner_var = outer_var 

Il problema riscontrato è dovuta a questo:

A block is a piece of Python program text that is executed as a unit. The following are blocks: a module, a function body, and a class definition.
(...)
A scope defines the visibility of a name within a block.
(...)
The scope of names defined in a class block is limited to the class block; it does not extend to the code blocks of methods – this includes generator expressions since they are implemented using a function scope. This means that the following will fail:

class A: 

     a = 42 

     b = list(a + i for i in range(10)) 

http://docs.python.org/reference/executionmodel.html#naming-and-binding

I suddetti mezzi:
corpo di una funzione è un blocco di codice e un metodo è una funzione, quindi nomi definiti dal corpo funzione presente in una definizione di classe non si estendono al corpo funzione.

Parafrasando questo per il tuo caso:
una definizione di classe è un blocco di codice, quindi i nomi definiti dalla definizione di classe interna presente in una definizione di classe esterna non si estendono alla definizione di classe interna.

+2

Incredibile. Il tuo esempio fallisce, sostenendo che "nome globale" a "non è definito". Tuttavia, sostituendo una list comprehension '[a + i per i in range (10)]' collega correttamente A.b alla lista attesa [42..51]. – George

+5

@George Si noti che l'esempio con ** classe A ** non è mio, è dal documento ufficiale di Python di cui ho fornito il collegamento. Questo esempio ha esito negativo e quell'errore è ciò che si desidera mostrare in questo esempio. In effetti '' list (a + i per i in range (10)) '' is '' list ((a + i per i in range (10))) '' vale a dire '' list (a_generator) ' '. Dicono che ** un generatore ** è implementato con un ambito simile rispetto all'ambito delle funzioni. – eyquem

+0

@George Per me, ciò significa che le funzioni si comportano in modo diverso a seconda se si trovano in un modulo o in una classe. Nel primo caso, una funzione va all'esterno per trovare l'oggetto associato a un identificatore libero. Nel secondo caso, una funzione, vale a dire un metodo, non esce dal suo corpo. Le funzioni in un modulo e i metodi in una classe sono in realtà due tipi di oggetti. I metodi non sono solo funzioni in classe. Questa è la mia idea – eyquem

4

soluzione più semplice:

class OuterClass: 
    outer_var = 1 
    class InnerClass: 
     def __init__(self): 
      self.inner_var = OuterClass.outer_var 

Si richiede di essere esplicito, ma non ci vuole molto sforzo.

+0

NameError: nome 'OuterClass' non definito - -1 –

+0

@Mr_and_Mrs_D Sì, vedere modifica. –

+1

Il punto è di accedervi dall'ambito della classe Outer - più un errore simile si verificherebbe se lo chiami da un ambito statico all'interno di Outer. Ti suggerisco di eliminare questo post –

1

In Python gli oggetti mutabili vengono passati come riferimento, quindi è possibile passare un riferimento della classe esterna alla classe interna.

class OuterClass: 
    def __init__(self): 
     self.outer_var = 1 
     self.inner_class = OuterClass.InnerClass(self) 
     print('Inner variable in OuterClass = %d' % self.inner_class.inner_var) 

    class InnerClass: 
     def __init__(self, outer_class): 
      self.outer_class = outer_class 
      self.inner_var = 2 
      print('Outer variable in InnerClass = %d' % self.outer_class.outer_var) 
+1

Tieni presente che hai un ciclo di riferimento qui, e in alcuni casi l'istanza di questa classe non verrà liberata. Un esempio, con cPython, se hai definito il metodo '__del__', il garbage collector non sarà in grado di gestire il ciclo di riferimento e gli oggetti andranno in' gc.garbage'. Il codice sopra, così com'è, non è tuttavia problematico. Il modo di affrontarlo utilizza un ** riferimento debole **. Puoi leggere la documentazione su [weakref (2.7)] (https://docs.python.org/2/library/weakref.html) o [weakref (3.5)] (https://docs.python.org/3.5/ biblioteca/weakref.html) – guyarad

0

Tutte le spiegazioni possono essere trovati in Python Documentazione Il Python Tutorial

Per il vostro primo errore <type 'exceptions.NameError'>: name 'outer_var' is not defined. La spiegazione è:

There is no shorthand for referencing data attributes (or other methods!) from within methods. I find that this actually increases the readability of methods: there is no chance of confusing local variables and instance variables when glancing through a method.

citato da Il Python Tutorial 9,4

Per il secondo errore <type 'exceptions.NameError'>: name 'OuterClass' is not defined

When a class definition is left normally (via the end), a class object is created.

citato da Il Python Tutorial 9.3.1

Così

quando provi il inner_var = Outerclass.outer_var, il Quterclass non è stato ancora creato, è per questo che name 'OuterClass' is not defined

Una spiegazione più dettagliata, ma noioso per il vostro primo errore:

Although classes have access to enclosing functions’ scopes, though, they do not act as enclosing scopes to code nested within the class: Python searches enclosing functions for referenced names, but never any enclosing classes. That is, a class is a local scope and has access to enclosing local scopes, but it does not serve as an enclosing local scope to further nested code.

citato da Learning.Python (5 °) .Mark.Lutz

Problemi correlati