2010-09-14 19 views
9

Je veux contrôler les variables globales (ou les variables globales) de telle sorte qu'elles ne soient définies qu'une seule fois dans le code d'initialisation du programme, et les verrouiller ensuite.Puis-je empêcher la modification d'un objet en Python?

J'utilise UPPER_CASE_VARIABLES pour les variables globales, mais je veux avoir un moyen sûr de ne pas changer la variable de toute façon.

  • Est-ce que python fournit cette fonctionnalité (ou similaire)?
  • Comment contrôlez-vous les variables de portée globale?
+4

Pourquoi vous passer du temps à ce sujet? Les gens ont votre source Python. Ils peuvent juste le changer. Pourquoi se tromper avec "final" et "constant"? –

+1

Vous savez, c'est exprès. Les gars de Python sont d'avis que nous sommes tous des adultes et que nous devrions agir comme tels. Alors pourquoi interdire l'accès du tout? Agissez comme un adulte et ne le changez jamais dans votre code. Si un autre programmeur le fait, c'est sa faute, si le code se casse, et vous avez un VCS pour savoir qui blâmer (je suppose). – Boldewyn

+1

En outre, c'est une chose facile à vérifier. Toute instance de 'UPPER_CASE =' est une erreur car quelqu'un a enfreint les règles. –

Répondre

11

ActiveState a une recette intitulée Constants in Python par le vénérable Alex Martelli pour la création d'un module const avec des attributs qui ne peuvent pas être reprise après la création. Cela ressemble à ce que vous recherchez, sauf pour la mise à jour — mais cela pourrait être ajouté en le faisant vérifier pour voir si le nom de l'attribut était tout en majuscule ou pas.

Bien sûr, cela peut être contourné par le déterminé, mais c'est la façon dont Python est — et est considéré comme une «bonne chose» par la plupart des gens. Cependant, pour le rendre un peu plus difficile, je suggère que vous ne vous embêtez pas à ajouter la méthode supposément évidente __delattr__ puisque les gens pourraient alors simplement supprimer les noms et ensuite les rajouter à différentes valeurs.

C'est ce que je prends sur:

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

Sortie:

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

Juste une remarque: Si vous importez une telle "constante" dans l'espace de noms local, ce n'est plus "constant". Le nom local peut facilement être rebondi. Et après tout, l'objet module peut toujours être rebondi. – lunaryorn

+0

@lunaryorn: Tout à fait vrai. La recette apparaît également dans "Python Cookbook, 2d ed" et là Martelli mentionne le fait que la valeur liée peut encore être changée si elle est mutable, comme une liste. Pour éviter qu'il mentionne une autre recette dans le livre qui permet d'envelopper des objets mutables et de les rendre en lecture seule. Cette autre recette est intitulée "Déléguer automatiquement comme une alternative à l'héritage" et il y a beaucoup de matériel dans sa section de discussion - plus que ce que je pensais être approprié de mettre dans ma réponse - donc je n'y suis pas allé ... – martineau

+0

'object .__ setattr __ (const, 'this_is_not_a_costant', 88)' .permet de définir une constante invalide ou de redéfinir une constante. Une astuce similaire est également disponible en utilisant la modification '__class__', qui peut être implémentée sans utiliser' object .__ setattr__' ('class o (objet): pass' suivi de' object .__ dict __ ['__ classe __'] .__ set __ (const, o) '), après quoi on peut définir des attributs comme une classe ordinaire. – ppperry

2

Vous pouvez envelopper vos variables globales dans un objet et remplacer la méthode .__ setattr__ de l'objet. Vous pouvez ensuite empêcher le paramétrage des attributs déjà définis. Cependant, cela ne concerne pas les objets complexes transmis par référence. Vous devez faire des copies superficielles/profondes de ces objets pour être absolument sûr qu'ils ne peuvent pas être modifiés. Si vous utilisez de nouvelles classes de style, vous pouvez remplacer l'objet .__ getattribute __ (self, name) pour faire les copies.

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

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

** Habituellement, je ne m'inquiète pas trop si quelqu'un essaye vraiment de casser mon code. Je trouve que substituer __setattr__ est suffisant (en particulier si vous lancez une exception) pour avertir quiconque joue avec le code que l'objectif de l'état doit être lu uniquement. Si quelqu'un ressent toujours le besoin de modifier l'état, tout comportement indéfini qu'il rencontre n'est pas de ma faute.

+1

Et qu'est-ce qui empêcherait quelqu'un d'écraser l'objet? – mikerobi

+0

Bon point: P. Je suppose qu'il n'y a pas grand-chose à faire? Peut-être un singleton? – GWW

+4

Chaque fois que quelqu'un essaie d'émuler des constantes, des membres privés, un contrôle de type nominatif, etc ... en Python (ou d'autres langages dynamiques), il échoue. Quand les gens apprendront-ils? (Une classe singleton peut être redéfinie tout aussi facilement) – delnan

6

Python est un langage très ouvert et ne contient pas de mot-clé final. Python vous donne plus de liberté pour faire les choses et suppose que vous savez comment les choses devraient fonctionner. Par conséquent, il est supposé que les personnes utilisant votre code sauront que SOME_CONSTANT ne devrait pas recevoir de valeur à un point aléatoire du code.

Si vous le souhaitez, vous pouvez inclure la constante dans une fonction getter.

def getConstant() 
    return "SOME_VALUE" 
+5

Il est encore possible (et seulement légèrement plus compliqué) de faire 'getConstant = lambda: new_value'. Et puisque 'ALL_CAPS' est la convention pour les constantes devrait-être, vous devriez simplement s'en tenir à cela. – delnan

Questions connexes