2010-09-14 9 views
9

Quiero controlar las variables globales (o las variables de ámbito global) de forma que se establezcan solo una vez en el código de inicialización del programa, y ​​bloquearlas después de eso.¿Puedo evitar la modificación de un objeto en Python?

Uso UPPER_CASE_VARIABLES para variables globales, pero quiero tener una manera segura de no cambiar la variable de todos modos.

  • ¿Python proporciona esa característica (o similar)?
  • ¿Cómo se controlan las variables con ámbito global?
+4

¿Por qué está gastando tiempo en esto? La gente tiene tu fuente de Python. Ellos solo pueden cambiarlo. ¿Por qué perder el tiempo con "final" y "constante"? –

+1

Ya sabes, eso es a propósito. Los chicos de Python son de la opinión, que todos somos adultos y debemos actuar como tal. Entonces, ¿por qué prohibir el acceso? Actúa como un adulto y nunca los cambies en tu código. Si otro programador lo hace, es su culpa, si el código se rompe, y usted tiene un VCS para descubrir a quién culpar (supongo). – Boldewyn

+1

Además, es una tarea fácil de auditar. Cualquier instancia de 'UPPER_CASE =' es un error porque alguien rompió las reglas. –

Respuesta

11

Activestate tiene una receta titulado Constants in Python por el venerable Alex Martelli para la creación de un módulo de const con atributos que no pueden recuperarse después de la creación. Eso suena como lo que estás buscando, excepto por el upsideing —, pero eso podría agregarse haciendo que verifique si el nombre del atributo estaba en mayúsculas o no.

Por supuesto, esto se puede eludir por el determinado, pero esa es la forma en que Python es — y es considerado como un "bien" por la mayoría de la gente. Sin embargo, para hacerlo un poco más difícil, le sugiero que no se moleste en agregar el método supuestamente obvio __delattr__, ya que las personas podrían simplemente eliminar nombres y luego volver a agregarlos a diferentes valores.

Esto es lo que estoy tomando sobre: ​​

# 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!") 

Salida:

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

Solo una observación: si importa una de esas "constantes" en el espacio de nombres local, ya no es "constante". El nombre local puede rebotar fácilmente. Y, después de todo, el objeto del módulo siempre puede ser rebote. – lunaryorn

+0

@lunaryorn: Muy cierto. La receta también aparece en "Python Cookbook, 2d ed" y allí Martelli menciona el hecho de que el valor encuadernado puede cambiarse si es mutable, como una lista. Para evitar eso, menciona otra receta en el libro que le permite a uno envolver objetos mutables y hacerlos de solo lectura. Esta otra receta se titula "Delegar automáticamente como una alternativa a la herencia" y hay mucho material en su sección de Discusión, más de lo que creí apropiado poner en mi respuesta, así que no fui allí ... – martineau

+0

'object .__ setattr __ (const, 'this_is_not_a_costant', 88)' .will funcionará para establecer una constante no válida o redefinir una constante. Un truco similar también está disponible usando la modificación '__class__', que puede implementarse sin usar' object .__ setattr__' ('class o (object): pass' seguido por' object .__ dict __ ['__ class __'] .__ set __ (const, o) '), después de lo cual uno puede establecer atributos como una clase ordinaria. – ppperry

2

Puede envolver sus variables globales en un objeto y anular el objeto .__ método setattr__. Luego puede evitar la configuración de atributos que ya están configurados. Sin embargo, esto no se trata de objetos complejos que se pasan por referencia. Tendría que hacer copias superficiales/profundas de esos objetos para estar absolutamente seguro de que no se pueden modificar. Si está utilizando nuevas clases de estilo, puede anular el objeto .__ getattribute __ (self, name) para hacer las copias.

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

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

** Normalmente no me preocupo tanto si alguien va a intentar realmente romper mi código. Considero que anular __setattr__ es suficiente (especialmente si arrojas una excepción) para advertir a quien esté jugando con el código que el objetivo para el Estado es solo leer. Si alguien todavía siente la necesidad de modificar el estado, cualquier comportamiento indefinido que encuentre no es mi culpa.

+1

¿Y qué evitaría que alguien sobrescribiera el objeto? – mikerobi

+0

Buen punto: P. ¿Supongo que no se puede hacer mucho? Tal vez un singleton? – GWW

+4

Cada vez que alguien intenta emular constantes, miembros privados, comprobación de tipo nominativo, etc. ... en Python (u otros lenguajes dinámicos), falla. ¿Cuándo la gente aprenderá? (Una clase singleton puede redefinirse igual de fácil) – delnan

6

Python es un lenguaje muy abierto y no contiene una palabra clave final. Python te da más libertad para hacer cosas y supone que sabes cómo deberían funcionar las cosas. Por lo tanto, se supone que las personas que usan su código sabrán que al SOME_CONSTANT no se le debe asignar un valor en algún punto al azar en el código.

Si realmente desea, puede encerrar la constante dentro de una función getter.

def getConstant() 
    return "SOME_VALUE" 
+5

Todavía es posible (y solo un poco más complicado) hacer 'getConstant = lambda: new_value'. Y dado que 'ALL_CAPS' es la convención para las constantes que deben ser, simplemente debe apegarse a eso. – delnan

Cuestiones relacionadas