2010-09-14 13 views
9

Voglio controllare le variabili globali (o le variabili globali) in modo che vengano impostate una sola volta nel codice di inizializzazione del programma e bloccarle successivamente.Posso impedire la modifica di un oggetto in Python?

Io uso UPPER_CASE_VARIABLES per le variabili globali, ma voglio avere un modo sicuro di non modificare la variabile comunque.

  • Python fornisce questa funzionalità (o simile)?
  • Come si controllano le variabili con ambito globale?
+4

Perché stai spendendo tempo su questo? Le persone hanno la tua fonte Python. Possono solo cambiarlo. Perché scherzare con "finale" e "costante"? –

+1

Sai, è così apposta. I ragazzi di Python sono dell'opinione che siamo tutti adulti e dovremmo comportarci come tali. Allora perché proibire l'accesso? Agisci come un adulto e non cambiarlo mai nel tuo codice. Se un altro programmatore lo fa, è colpa sua, se il codice si rompe, e tu hai un VCS per scoprire chi dare la colpa (presumo). – Boldewyn

+1

Inoltre, è una cosa facile da controllare. Qualsiasi istanza di 'UPPER_CASE = 'è un errore perché qualcuno ha infranto le regole. –

risposta

11

ActiveState ha una ricetta intitolata Constants in Python dal venerabile Alex Martelli per la creazione di un modulo const con gli attributi che non possono essere rimbalzo dopo la creazione. Sembra che quello che stai cercando eccetto per l'aggiornamento — ma che potrebbe essere aggiunto facendolo controllare per vedere se il nome dell'attributo era tutto in maiuscolo o meno.

Naturalmente, questo può essere aggirato dal determinato, ma questo è il modo in cui Python è — ed è considerato una "cosa buona" dalla maggior parte della gente. Tuttavia, per renderlo un po 'più difficile, ti suggerisco di non preoccuparti di aggiungere il metodo apparentemente ovvio __delattr__ poiché le persone potrebbero semplicemente cancellare i nomi e quindi aggiungerli nuovamente a valori diversi.

Questo è quello che sto prendendo circa:

# Put in const.py... 
# from http://code.activestate.com/recipes/65207-constants-in-python 
class _const: 
    class ConstError(TypeError): pass # base exception class 
    class ConstCaseError(ConstError): pass 

    def __setattr__(self, name, value): 
     if name in self.__dict__: 
      raise self.ConstError("Can't change const.%s" % name) 
     if not name.isupper(): 
      raise self.ConstCaseError('const name %r is not all uppercase' % name) 
     self.__dict__[name] = value 

# replace module entry in sys.modules[__name__] with instance of _const 
# (and create additional reference to module so it's not deleted -- 
# see Stack Overflow question: http://bit.ly/ff94g6) 
import sys 
_ref, sys.modules[__name__] = sys.modules[__name__], _const() 

if __name__ == '__main__': 
    import const # test this module... 

    try: 
     const.Answer = 42 # not OK, mixed-case attribute name 
    except const.ConstCaseError as exc: 
     print(exc) 
    else: # test failed - no ConstCaseError exception generated 
     raise RuntimeError("Mixed-case const names should't have been allowed!") 

    const.ANSWER = 42 # should be OK, all uppercase 

    try: 
     const.ANSWER = 17 # not OK, attempts to change defined constant 
    except const.ConstError as exc: 
     print(exc) 
    else: # test failed - no ConstError exception generated 
     raise RuntimeError("Shouldn't have been able to change const attribute!") 

uscita:

const name 'Answer' is not all uppercase 
Can't change const.ANSWER 
+0

Solo un commento: se si importa una "costante" nel namespace locale, non è più "costante". Il nome locale può facilmente essere rimbalzato. E dopo tutto, l'oggetto modulo può sempre essere in estensione. – lunaryorn

+0

@lunaryorn: abbastanza vero. La ricetta appare anche in "Python Cookbook, 2d ed" e lì Martelli menziona il fatto che il valore associato può ancora essere cambiato se è mutabile, come una lista. Per evitare che menzioni un'altra ricetta nel libro che consente di avvolgere oggetti mutabili e renderli di sola lettura. Quest'altra ricetta si intitola "Delegare automaticamente come alternativa all'ereditarietà" e nella sua sezione di discussione c'è molto materiale, più di quanto mi sarebbe sembrato opportuno inserire nella mia risposta, quindi non ci sono andato ... – martineau

+0

'object .__ setattr __ (const, 'this_is_not_a_costant', 88)'. Funzionerà per impostare una costante non valida o ridefinire una costante. Un trucco simile è anche disponibile usando la modifica di '__class__', che può essere implementata senza usare' oggetto .__ setattr__' ('classe o (oggetto): pass' seguito da' oggetto .__ dict __ ['__ classe __'] .__ set __ (const, o) '), dopo di che si possono impostare attributi come una classe ordinaria. – ppperry

2

È possibile racchiudere le variabili globali in un oggetto e sovrascrivere il metodo oggetto .__ setattr__. È quindi possibile impedire l'impostazione degli attributi già impostati. Tuttavia, questo non riguarda oggetti complessi che vengono passati per riferimento. Dovresti fare copie poco profonde/profonde di quegli oggetti per essere assolutamente sicuro che non possano essere modificati. Se si utilizzano nuove classi di stile, è possibile sovrascrivere l'oggetto .__ getattribute __ (self, name) per creare le copie.

class State(object): 
    def __init__(self): 
     pass 

    def __setattr__(self, name, value): 
     if name not in self.__dict__: 
      self.__dict__[name] = value 

** Io di solito non mi preoccupo tanto se qualcuno sta per provare davvero difficile da rompere il mio codice. Trovo che __setattr__ sia sufficiente (specialmente se si lancia un'eccezione) per avvisare chi sta giocando con il codice che l'obiettivo per lo stato deve essere letto solo. Se qualcuno sente ancora il bisogno di modificare lo stato, qualsiasi comportamento non definito che incontrano non è colpa mia.

+1

E cosa impedirebbe a qualcuno di sovrascrivere l'oggetto? – mikerobi

+0

Buon punto: P. Suppongo che non ci sia molto da fare? Forse un singleton? – GWW

+4

Ogni volta che qualcuno tenta di emulare costanti, membri privati, controllo di tipo nominativo, ecc. In Python (o in altri linguaggi dinamici), fallisce. Quando impareranno le persone? (Una classe singleton può essere ridefinita altrettanto facilmente) – delnan

6

Python è una lingua molto aperta e non contiene una parola chiave final. Python ti dà più libertà di fare cose e presuppone che tu sappia come dovrebbero funzionare le cose. Pertanto, si presume che chi usa il tuo codice saprà che a SOME_CONSTANT non dovrebbe essere assegnato un valore in un punto casuale del codice.

Se lo si desidera, è possibile racchiudere la costante all'interno di una funzione getter.

def getConstant() 
    return "SOME_VALUE" 
+5

È ancora possibile (e solo marginalmente più complicato) fare 'getConstant = lambda: new_value'. E poiché 'ALL_CAPS' è la convenzione per le costanti dovresti essere, dovresti limitarti a quello. – delnan

Problemi correlati