Welcome, guest | Sign In | My Account | Store | Cart

In Python, any variable can be re-bound at will -- and modules don't let you define special methods such as an instance's __setattr__ to stop attribute re-binding. Easy solution (in Python 2.1 and up): use an instance as "module"...

Python, 17 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# Put in const.py...:
class _const:
    class ConstError(TypeError): pass
    def __setattr__(self,name,value):
        if self.__dict__.has_key(name):
            raise self.ConstError, "Can't rebind const(%s)"%name
        self.__dict__[name]=value
import sys
sys.modules[__name__]=_const()

# that's all -- now any client-code can
import const
# and bind an attribute ONCE:
const.magic = 23
# but NOT re-bind it:
const.magic = 88      # raises const.ConstError
# you may also want to add the obvious __delattr__

In Python 2.1 and up, no check is made any more to force entries in sys.modules to be actually module objects. You can install an instance object there and take advantage of its attribute-access special methods (e.g., as in this snippet, to prevent rebindings; probably more useful, to synthesize attributes on the fly in a __getattr__; whatever...), while still having client-code get the thing with "import whatever". You may also choose to see this as a more Pythonic 'singleton' pattern, or, rather, idiom:-) (but, regarding singleton, also see http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66531).

13 comments

philip nunez 22 years, 5 months ago  # | flag

good for typechecking too?

class typecheck:
    def __init__(self,i):
        self.__type__ = type(i)
    def __setattr__(self,name,value):
        if (name.find('__type__') != 0) and (type(value) != self.__type__):
            raise TypeError, "(%s) is not %s"%(name,self.__type__.__name__)
        self.__dict__[name]=value


if __name__ == '__main__':
    import sys
    try:
        my_int = typecheck(1)
        my_int.x = 3
        my_int.y = 4
        my_int.z = 5
        my_int.h = 'hey'
    except:
        print sys.exc_type, sys.exc_value

    try:
        my_string = typecheck('')
        my_int.s = 'hello'
        my_int.t = 'byebye'
        my_int.u = 3
    except:
        print sys.exc_type, sys.exc_value
Jason Orendorff 22 years, 2 months ago  # | flag

Don't get carried away, though. This is clever, but keep in mind that all the standard modules implement constants this way:

AUDIO_FILE_MAGIC = 0x2e736e64
AUDIO_FILE_ENCODING_MULAW_8 = 1
AUDIO_FILE_ENCODING_LINEAR_8 = 2
AUDIO_FILE_ENCODING_LINEAR_16 = 3

This is much clearer to me. (It runs faster, too.)

Michael Shepanski 18 years, 10 months ago  # | flag

Jason Orendorff, What prevents someone from re-declaring your variables?

marijn haverbeke 18 years, 8 months ago  # | flag

The fact that they are all-uppercase and that the programmer knows that means they are intended to be constants.

If the programmer is a moron, you're screwed anyway. I find all this tendency to try and create libraries/frameworks/utils that can not possibly be used incorrectly a little wrong-headed. Just follow conventions, document, and hope for the best.

Stephen Chappell 18 years, 5 months ago  # | flag

except: Just make sure that you don't follow that philosophy when you are creating your end product. Your end-user should be able to crash the product. They should, instead, get warnings, if anything.

Thomas Ahle 16 years, 9 months ago  # | flag

Good philosophy. This philosophy is the reason I like python. No need for restrictive private functions and stuff. It should be clear to people that this function is not supposed to be called, but if they REALLY want to, they can do it. And yes, this has nothing to do with the end user, which should of course be protected against h(im|er)self.

chris skoni 15 years, 10 months ago  # | flag

Error after running the module. After typing at prompt >>> : import const I get this error: Traceback (most recent call last): File "", line 1, in -toplevel- import const ImportError: No module named const

I also tried >>> import _const (because definition has _const (not const)) - but the same error.

So how does it work ?

THIS IS THE CODE that I RAN: class const: class ConstError(TypeError): pass def __setattr__(self,name,value): if self.__dict__.has_key(name): raise self.ConstError, "Can't rebind const(%s)"%name self.__dict__[name]=value import sys sys.modules[__name__]=_const()

-thanks-cs-

dharkness 15 years, 5 months ago  # | flag

Did you create a file called const.py containing lines 2 through 9, inclusive? Did you place the file somewhere where the import would see it?

Brainiac 5 14 years, 10 months ago  # | flag

So there are no constants. Ok.

But could that ever in the python world go without praising the unique philosophy or laughing at the question (#1 Google hit: "Python doesn't need no steenkeeng conztantz!") at the expense of the asking and to underline the grandeur of python and it's cool users?

And why people may look for constants, there may be other's like me, thinking, well, the compiler could be faster if it could write the literal number right in there when I use it. So how do I tell it, it can. Maybe the Python compiler knows how to optimize that. Maybe it doesn't and the cycles for a needless reference are just wasted; or the optimal code, using the literal, is one that is not legible to a human? Maybe I'm missing a point and I should use tupels in some way I am just now looking for?

Xiong Chiamiov 14 years, 9 months ago  # | flag

@Brainiac 5: If the only reason for adding a feature is to get a tiny little bit faster in compiling _some_ python scripts, then it's not going to get added.

It's not your job to do compiler optimizations. Pythonically, you should be concerned with readability, flexibility, and in general, what works the best for you, rather than how any of those choices will affect the compiler. In general, optimizing in Python means revisiting your algorithms; after all, there are plenty of things Python does quite frequently that make C programmers shudder at the inefficiency, and the impact of those is far greater than your constant declarations.

Jonathan Gregson 12 years ago  # | flag

I found this really useful for an HTTP server I'm building, and was wondering if I could have this code under GPL v2? I need this to prevent an attacker from changing the server's configuration during runtime. Here is the project page if you want to see it. Also, I am somewhat new to python, so feel free to critique the code. https://launchpad.net/httpy

Jonathan Gregson 12 years ago  # | flag

Never mind, I just noticed that it's licensed under the PSF license. Thanks.

Martin Miller 10 years, 7 months ago  # | flag

minor caveat

FWIW, note that in Python 2.7.x, the value of __name__ gets changed to None following execution of the sys.modules[__name__]=_const() statement.

This can cause problems if you refer to __name__ again is subsequent code, such as shown in the related question about this potentially undesirable side effect posted on stackoverflow. The accepted answer to the question explains why it happens and a simple work-around.