2011-10-28 25 views
38

Supponiamo che io ho il seguente codice Python:Python sovrascrivere le variabili in funzioni nidificate

def outer(): 
    string = "" 
    def inner(): 
     string = "String was changed by a nested function!" 
    inner() 
    return string 

Voglio una chiamata a esterno() per tornare! "String è stato cambiato da una funzione annidata", ma ottengo "" . Concludo che Python pensa che la riga string = "string was changed by a nested function!" sia una dichiarazione di una nuova variabile locale a inner(). La mia domanda è: come faccio a dire a Python che dovrebbe usare la stringa outer()? Non riesco a utilizzare la parola chiave global, perché la stringa non è globale, ma vive in un ambito esterno. Idee?

+0

potrebbe essere correlato: http://stackoverflow.com/q/146359/212218 –

risposta

35

In Python 3.x, è possibile utilizzare la parola chiave nonlocal:

def outer(): 
    string = "" 
    def inner(): 
     nonlocal string 
     string = "String was changed by a nested function!" 
    inner() 
    return string 

In Python 2.x, è possibile utilizzare una lista con un unico elemento e sovrascrivere quel singolo elemento:

def outer(): 
    string = [""] 
    def inner(): 
     string[0] = "String was changed by a nested function!" 
    inner() 
    return string[0] 
+0

+1 Per quanto riguarda il motivo quest'ultimo funziona: l'assegnazione degli oggetti (insieme all'assegnazione degli attributi) non è un'assegnazione variabile - 'x [...] = ...' prende semplicemente 'x' da qualunque ambito si tratterebbe in ogni caso, e call è il suo metodo' __setitem__' . Un'opzione altrettanto valida, ma inizialmente più prolissa, è la creazione di un oggetto (di una classe definita dall'utente, dato che 'object()' non consente di aggiungere attributi) il cui unico scopo è quello di mantenere un attributo. – delnan

+2

È possibile creare un tale "oggetto spazio dei nomi" usando 'x = type (" ",(), {})()'. :) –

+0

Quindi, 'nonlocal' significa:" cerca questa variabile nello scope esterno "- se la variabile non è stata trovata nello scope esterno(), Python continuerà a cercare negli ambiti che racchiudono? Python alla fine cercherà di trovare la variabile nell'ambito globale? – Ord

2

per aggiungere a Sven's answer:

In Python 2.x, si può solo leggere variabili ambito esterno dalla locanda er ambito. L'assegnazione creerà solo una nuova variabile locale (cioè l'ambito interno) che nasconde l'ambito esterno.

Se si desidera leggere e modificare, è possibile utilizzare un dict di tenere le variabili in ambito esterno, e quindi accedervi tramite il dict del perimetro interno, anche mantenendo il codice abbastanza pulita e leggibile in presenza di multipla ambito esterno vars:

def outer(): 
    # hold some text, plus the number of spaces in the text 
    vars = {'text': 'Some text.', 'num_spaces': 1} 
    def inner(): 
     # add some more text 
     more_text = ' Then some more text.' 
     vars['text'] += more_text 
     # keep track of the number of spaces 
     vars['num_spaces'] += more_text.count(' ') 
    inner() 
    return vars['text'], vars['num_spaces'] 

uscita:

>>> outer() 
('Some text. Then some more text.', 5) 
19

È inoltre possibile aggirare questo ostacolo utilizzando la funzione attributi:

def outer(): 
    def inner(): 
     inner.string = "String was changed by a nested function!" 
    inner.string = "" 
    inner() 
    return inner.string 

Precisazione: questo funziona sia in 2.xe 3.x. pitone

+3

Questa è in realtà un'idea eccellente. – Alice

3

Questo mi succede troppo spesso, mentre scrivevo una funzione e all'improvviso mi rendo conto che potrebbe essere una buona idea avere una funzione di supporto più piccola, ma non molto utile da nessun'altra parte. che naturalmente mi fa desiderare di definirlo all'interno come una funzione annidata.

ma ho avuto esperienza con oggetto anonimo JAVA (ad esempio: definire un eseguibile), e la regola era che l'oggetto anonimo rende una copia del proprio ambiente esterno, in questo caso le variabili dello scope esterno. Pertanto, se la variabile esterna è immutabile (int, char), essi non possono essere modificati per oggetto anonimo come vengono copiati da valore che se un mutevole (collection, objects), essi possono essere cambiati ... poiché vengono copiati dal "puntatore" (il loro indirizzo in memoria)

se si conosce la programmazione, pensarla come valore per passare e passare per riferimento.

in python, è praticamente lo stesso.x=123 è un incarico, danno il senso nuovo xa variabile (non modificare il vecchio x), list[i]/dict[key] sono operazioni di accesso agli oggetti, in realtà modificano cose

per concludere, è necessario un oggetto mutabile ... al fine di modificare (anche se è possibile accedere a una tupla usando [], non è possibile utilizzarlo qui poiché non è mutabile)

Problemi correlati