2015-06-24 11 views
8

In breve, la domanda: C'è un modo per impedire a Python di cercare variabili al di fuori dell'ambito corrente?Disabilitare la ricerca di variabili globali in Python

Dettagli:

Python guarda per le definizioni delle variabili in ambiti esterni se non sono definite nell'ambito corrente. Così, il codice come questo rischia di rompersi quando non facendo attenzione durante il refactoring:

def line(x, a, b): 
    return a + x * b 

a, b = 1, 1 
y1 = line(1, a, b) 
y2 = line(1, 2, 3) 

Se ho rinominato gli argomenti della funzione, ma ho dimenticato di rinominarle all'interno del corpo della funzione, il codice sarebbe ancora eseguito:

def line(x, a0, b0): 
    return a + x * b # not an error 

a, b = 1, 1 
y1 = line(1, a, b) # correct result by coincidence 
y2 = line(1, 2, 3) # wrong result 

Conosco gli ambiti esterni it is bad practice to shadow names from. Tuttavia, ci sono alcune ragioni per cui questo viene fatto in ogni caso:

  • A volte può essere utile per avere gli stessi nomi perché si riferiscono alla stessa cosa
  • Si potrebbe a corto di nomi di variabili significative
  • pigrizia

C'è un modo per impedire a Python di cercare variabili al di fuori dell'ambito corrente? (So ​​che l'accesso a o b genera un errore nel secondo esempio.)

A causa di essere pigro, preferirei una soluzione che funziona senza codice caldaia-piastra ripetuto :)

Se il problema è ambiguo in termini di versione Python, sono principalmente interessato a Python 3.3 e versioni successive.

+2

No, non c'è. Altrimenti nessun built-in funzionerebbe mai. –

+1

La soluzione migliore è usare * test appropriato *, piuttosto che cercare di violare Python. –

+2

metti le ultime tre linee in un'altra funzione ... – LittleQ

risposta

3

No, non si può dire a Python di non guardare i nomi in ambito globale.

Se fosse possibile, non sarà possibile utilizzare qualsiasi altre classi o funzioni definite nel modulo, nessun oggetto importato da altri moduli, né si potrebbero utilizzare nomi predefiniti. Il tuo spazio dei nomi delle funzioni diventa un deserto privo di quasi tutto ciò di cui ha bisogno e l'unica via d'uscita sarebbe quella di importare tutto nello spazio dei nomi locale. Per ogni singola funzione nel modulo.

Anziché cercare di interrompere le ricerche globali, mantenere pulito lo spazio dei nomi globale. Non aggiungere elementi globali che non è necessario condividere con altri ambiti nel modulo. Ad esempio, utilizza una funzione main() per incapsulare quelli che sono in realtà solo i locali.

Inoltre, aggiungere unittesting. Refactoring senza (anche solo alcuni) test è sempre incline a creare bug altrimenti.

+2

Ho sempre considerato l'uso di una funzione 'main()' negli script Python un relitto della vecchia pratica C. Pensare in termini di inquinamento globale dello spazio dei nomi mi fa vedere in una luce diversa, ora. Grazie :) – kazemakase

1

Per scoraggiare la ricerca delle variabili globali, spostare la funzione in un altro modulo. A meno che non ispezioni lo stack di chiamate o importi il ​​modulo di chiamata in modo esplicito; non avrà accesso ai globali dal modulo che lo chiama.

In pratica, spostare il codice in una funzione main(), per evitare di creare variabili globali non necessarie.

Se si utilizzano i globali perché diverse funzioni devono modificare lo stato condiviso, spostare il codice in una classe.

8

Sì, forse non in generale. Tuttavia puoi farlo con le funzioni.

La cosa che si vuole fare è avere la funzione globale di essere vuota. Non è possibile sostituire i globali e non si desidera modificarne il contenuto (perché lo sarebbe solo per sbarazzarsi delle variabili e delle funzioni globali).

Tuttavia: è possibile creare oggetti funzione in runtime. Il costruttore ha l'aspetto di types.FunctionType((code, globals[, name[, argdefs[, closure]]]). Ci si può sostituire il namespace globale:

def line(x, a0, b0): 
    return a + x * b # will be an error 

a, b = 1, 1 
y1 = line(1, a, b) # correct result by coincidence 

line = types.FunctionType(line.__code__, {}) 
y1 = line(1, a, b) # fails since global name is not defined 

Ovviamente si può pulire questo definendo il proprio decoratore:

import types 
noglobal = lambda f: types.FunctionType(f.__code__, {}) 

@noglobal 
def f(): 
    return x 

x = 5 
f() # will fail 

A rigor di termini non vietarlo per accedere alle variabili globali, basta fare la funzione crede che non ci siano variabili nello spazio dei nomi globale. In realtà si può anche usare questo per emulare variabili statiche poiché se dichiara una variabile essere globale e assegnarla ad essa finirà nella propria sandbox di spazio dei nomi globale.

Se si desidera poter accedere a parte dello spazio dei nomi globale, è necessario popolare le funzioni sandbox globale con ciò che si desidera visualizzare.

+0

Qualcosa del genere era quello che avevo in mente inizialmente quando ho fatto la domanda. Tuttavia, utilizzare semplicemente una funzione principale si adatta meglio alle mie reali esigenze. – kazemakase

+0

Ottima risposta! L'ho esteso un po 'per mantenere le importazioni e le funzioni definite: https://gist.github.com/ax3l/59d92c6e1edefcef85ac2540eb056da3 – Ax3l