2012-09-28 16 views
9

Stack Overflow ha un sacco di domande riguardanti le variabili globali in python e sembra generare un po 'di confusione per le persone che provengono da altre lingue. Le regole per gli obiettivi non funzionano esattamente come molte persone di altri contesti si aspettano che facciano.Frequenza delle variabili globali in python?

Allo stesso tempo, il codice è pensato per essere organizzato non tanto a livello di classe, ma a livello di modulo. Quindi, quando tutto non è necessariamente contenuto nelle classi, lo stato che altrimenti verrebbe trovato nelle variabili membro può andare in variabili a livello di modulo.

Quindi la mia domanda è 2 parte:

1) Dovrei essere evitando l'uso di variabili globali (in particolare impostandole dall'interno funzioni e utilizzando la parola chiave globale)?

2) Se il n. 1 è sì, ci sono schemi comuni in cui dovrebbero essere utilizzati?

Io lavoro in un posto in cui abbondano molte lingue diverse e voglio mitigare la confusione e assicurarmi che i pitonisti non mi odieranno più tardi.

Grazie per qualsiasi input costruttivo.

+0

E c'è 'nonlocal' in Python 3.x :) –

+0

@ Jon Clements: E allora? – martineau

risposta

7

Consiglio vivamente di leggere questo post sul blog intitolato Singletons and their Problems in Python. Mi ha fatto ripensare al mio uso di variabili globali. Alcune citazioni a scelta:

Ma attenzione. Solo perché non si implementa il modello di progettazione singleton, ciò non significa che si eviti il ​​problema principale di un singleton. Il problema principale di un singleton è lo stato globale e condiviso. Un singleton non è altro che una variabile globale glorificata e in linguaggi come Java ci sono molte ragioni per cui si vorrebbe usare qualcosa come un singleton. In Python abbiamo qualcosa di diverso per i singleton, e ha un nome molto innocente che nasconde i dettagli cruenti: modulo.

Proprio così: un modulo Python è un singleton. E condivide gli stessi problemi del modello singleton solo che è un po 'peggio.

E qui è un esempio di problemi aventi tale stato condiviso potrebbe causare:

Al fine di non parlare di cose irrilevanti, diamo uno sguardo a uno dei moduli della libreria standard, il modulo mimetipi.

dare un'occhiata:

inited = False 

def init(files=None): 
    global inited 
    db = MimeTypes() 
    ... 

Questo è il codice vero e proprio dal modulo mimetypes fornito con Python, basta con i particolari più scabrosi rimossi. Il punto è, c'è uno stato condiviso. E lo stato condiviso è un flag booleano che è True se il modulo è stato inizializzato o False se non lo era. Ora, quel caso particolare probabilmente non è così problematico (credetemi, lo è) perché i mimetypes si inizializzano da soli, ma potete vedere che c'è un parametro files nella funzione init. Se si passa una lista di file a quella funzione, reinizializzerà il database mime in memoria con le informazioni mime di quei file. Ora immaginate che cosa accadrebbe se si dispone di due librerie di inizializzazione mimetypes con due fonti diverse ...

Questo è uno schema abbastanza comune, e l'ho fatto io ... ma per esempio un modo migliore per farlo sarebbe : init restituisce un'istanza di una classe che implementa tutti i metodi e altre parti del codice possono init per ottenere un'istanza diversa con parametri diversi che non interferiscono con la prima. Lo "svantaggio" è che devi passare questa istanza a qualsiasi codice che non vuole inizializzarne uno nuovo, ma il lato positivo di quello "svantaggio" è che rende ovvio quali sono le tue dipendenze.

Ad ogni modo, in breve, proverei a evitarlo il più possibile, ma se si sta bene con il codice che ha un singleton implicito, si consiglia di farlo.

+1

Un'altra limitazione importante dell'uso dei moduli come istanze di un singleton è che, diversamente da una classe con un metodo '__init __()' che verrà chiamato ed eventualmente passato argomenti, è difficile influenzare/controllare la costruzione dell'istanza (singola) creata sul suo 'import' iniziale. – martineau

+0

@Claudiu grazie, questa risposta mi ha dato più di quanto mi aspettassi :). – MrFox

2

Globali non sono davvero un tabù, è solo che devi ricordarti di dichiararli globali nella tua funzione prima di usarli. Secondo me questo li rende più chiari perché l'utente vede che stai utilizzando in modo esplicito un global.

Sarei più spaventato dal fatto che i non pitonisti non comprendano le globali di Python, modificando il codice e non aggiungendo le dichiarazioni globali appropriate.

3

In breve, sì, dovresti evitare di utilizzare la parola chiave global. Potrebbe essere nella lingua per una ragione, ma in genere considero l'odore del codice: se hai uno stato che vuoi tenere aggiornato, incapsalo in una classe. È molto meno fragile rispetto all'utilizzo di global.

+1

Considero una regola dura e veloce per non usare mai 'global'. Mantenere tutto il tuo stato in un'istanza di classe è abbastanza facile, e se hai mai bisogno di creare più di un'istanza di una classe, dovresti ringraziare le tue stelle fortunate che non le hai mai passate l'una sull'altra. Se lo stato ha davvero bisogno di essere comune, spostalo di un livello nella gerarchia delle classi. – Dave

+0

A volte tendo a fare 'foo = [globalvar]' e poi a ottenere e impostare 'foo [0]' all'interno delle funzioni, quindi non devo usare quella parola chiave 'global'. in qualche modo rende più ovvio che sto usando una variabile globale perché devo trattarlo in modo "speciale", ma è ancora piuttosto brutto, proprio come l'uso delle variabili globali è – Claudiu

3

ho fatto questa osservazione sulla vostra altra domanda, ecco le mie 2 centesimi:

Python è Object Oriented, ma non c'è bisogno di usarlo. Ma se si sceglie di, si potrebbe sfruttare questo meccanismo della classe di tenere alcune proprietà:

class Config(): 
""" 
hold some vars for example. 
""" 
def __init__(self): 
    self.CONFIG_LOG_FILE_DIR='/tmp2/ozn/venus_mon_log/' 
    self.DATE_FORMAT="%Y%m%d" 
    #self.FILE_NAME_BASE_TEMPLATE=eval('datetime.datetime.now().strftime(self.DATE_FORMAT)')+'-venus_utilization.log' 
    self.FILE_NAME_BASE_TEMPLATE='venus_utilization.log' 
    self.FILE_NAME=self.CONFIG_LOG_FILE_DIR+self.FILE_NAME_BASE_TEMPLATE 
    self.MAX_FILE_SIZE=1024*1024*50 # in Byte, 50 in MB. 

si potrebbe creare un'istanza che consente di accedere alle proprietà:

cfg = Config() 

E poi accedervi utilizzando :

cfg.MAX_FILE_SIZE 

o anche cambiarli:

cfg.MAX_FILE_SIZE=50000 
    cfg.MAX_FILE_SIZE=calculateNewSize() 

e così via ... Si potrebbe anche fare cose come:

# this will print all items that an instance has  
if options.debug: 
    print "DEBUG INFO:" 
    for k,v in vars(cfg).iteritems(): 
     print k,v