I want to control global variables (or globally scoped variables) in a way that they are set only once in program initialization code, and lock them after that.
I use UPPER_CASE_VARIABLES for global variables, but I want to have a sure way not to change the variable anyway.
- Does python provide that (or similar) feature?
- How do you control the globally scoped variables?
3 Answers 3
ActiveState has a recipe titled Cᴏɴsᴛᴀɴᴛs ɪɴ Pʏᴛʜᴏɴ by the venerable Alex Martelli for creating a const
module with attributes which cannot be rebound after creation. That sounds like what you're looking for except for the upcasing — but that could be added by making it check to see whether the attribute name was all uppercase or not.
Of course, this can be circumvented by the determined, but that's the way Python is — and is considered to be a "good thing" by most folks. However, to make it a little more difficult, I suggest you don't bother adding the supposedly obvious __delattr__
method since people could then just delete names and then add them back rebound to different values.
This is what I'm taking about:
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 it to prevent its deletion -- see
# https://stackoverflow.com/questions/5365562/why-is-the-value-of-name-changing-after-assignment-to-sys-modules-name)
import sys
_ref, sys.modules[__name__] = sys.modules[__name__], _const()
if __name__ == '__main__':
import __main__ as const # Test this module...
try:
const.Answer = 42 # Not OK to create 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 be allowed!")
try:
const.ANSWER = 42 # Should be OK, all uppercase.
except Exception as exc:
raise RuntimeError("Defining a valid const attribute should be allowed!")
else: # Test succeeded - no exception generated.
print('const.ANSWER set to %d raised no exception' % const.ANSWER)
try:
const.ANSWER = 17 # Not OK, attempt to change defined constant.
except const.ConstError as exc:
print(exc)
else: # Test failed - no ConstError exception generated.
raise RuntimeError("Shouldn't be able to change const attribute!")
Output:
const name 'Answer' is not all uppercase
const.ANSWER set to 42 raised no exception
Can't change const.ANSWER
-
Just a remark: If you import a such a "constant" into the local namespace, it's not longer "constant". The local name can easily be rebound. And after all, the module object can always be rebound.user355252– user3552522010年09月15日 08:51:46 +00:00Commented Sep 15, 2010 at 8:51
-
1@lunaryorn: Quite true. The recipe also appears in "Python Cookbook, 2d ed" and there Martelli mentions the fact that the value being bound can still be changed if it is mutable, such as a list. To prevent that he mentions another recipe in the book that allows one to wrap mutable objects and make them read-only. This other recipe is titled "Delegating Automatically as an Alternative to Inheritance" and there's a lot of material in its Discussion section -- more than I felt would be appropriate to put in my answer -- so I didn't go there...martineau– martineau2010年09月15日 13:10:13 +00:00Commented Sep 15, 2010 at 13:10
-
object.__setattr__(const,'this_is_not_a_costant',88)
.will work to set an invalid constant or redefine a constant. A similar trick is also available using__class__
modification, which can be implemented without usingobject.__setattr__
(class o(object):pass
followed byobject.__dict__['__class__'].__set__(const, o)
), after which one can set attributes like an ordinary class.Perry– Perry2016年05月30日 19:02:54 +00:00Commented May 30, 2016 at 19:02 -
@lunaryorn: FWIW, there's a version of that other recipe in the book I mentioned, which would allow one to wrap mutable objects and make them read-only, is also posted on ActiveState as Aᴜᴛᴏᴍᴀᴛɪᴄ ᴅᴇʟᴇɢᴀᴛɪᴏɴ ᴀs ᴀɴ ᴀʟᴛᴇʀɴᴀᴛɪᴠᴇ ᴛᴏ ɪɴʜᴇʀɪᴛᴀɴᴄᴇ.martineau– martineau2019年05月01日 16:16:39 +00:00Commented May 1, 2019 at 16:16
-
@pppery: As I alluded in my answer, there are ways for the determined to get around the restrictions—that's just the way Python is...martineau– martineau2019年05月01日 16:23:58 +00:00Commented May 1, 2019 at 16:23
Python is a very open language and does not contain a final
keyword. Python gives you more freedom to do things and assumes you know how things should work. Therefore, it is assumed that people using your code will know that SOME_CONSTANT
should not be assigned a value at some random point in the code.
If you really do want to, you can enclose the constant inside a getter function.
def getConstant()
return "SOME_VALUE"
-
8It's still possible (and only marginally more complicated) to do
getConstant = lambda: new_value
. And sinceALL_CAPS
is the convention for should-be constants, you should just stick to that.user395760– user3957602010年09月14日 18:25:02 +00:00Commented Sep 14, 2010 at 18:25
You could wrap your global variables in an object and override the object.__setattr__
method. You can then prevent setting attributes that are already set. However, this doesn't deal with complex objects that are passed by reference. You would need to make shallow/deep copies of those objects to be absolutely sure they can't be modified. If you are using new style classes you could override object.__getattribute__(self, name)
to make the copies.
class State(object):
def __init__(self):
pass
def __setattr__(self, name, value):
if name not in self.__dict__:
self.__dict__[name] = value
** I usually don't worry so much if someone is going to try really hard to break my code. I find overriding __setattr__
is sufficient (especially if you throw an exception) to warn whoever is playing with the code that the goal for the State
is to be read only. If someone still feels the need to modify the state then whatever undefined behavior they encounter isn't my fault.
-
1And what would prevent someone from overwriting the object?mikerobi– mikerobi2010年09月14日 18:19:40 +00:00Commented Sep 14, 2010 at 18:19
-
Good point :P. I suppose there's not much that can be done? Maybe a singleton?GWW– GWW2010年09月14日 18:21:30 +00:00Commented Sep 14, 2010 at 18:21
-
4Every time someone tries to emulate constants, private members, nominative type checking, etc... in Python (or other dynamic languages), he fails. When will people learn? (A singleton class can be redefined just as easy)user395760– user3957602010年09月14日 18:28:22 +00:00Commented Sep 14, 2010 at 18:28
-
1You are right, I guess I've never been that concerned about someone messing with my code.GWW– GWW2010年09月14日 18:35:23 +00:00Commented Sep 14, 2010 at 18:35
UPPER_CASE =
is an error because someone broke the rules.