How do I create a constant in Python?
Asked Answered
C

44

1386

How do I declare a constant in Python?

In Java, we do:

public static final String CONST_NAME = "Name";
Cyperaceous answered 21/4, 2010 at 12:20 Comment(7)
actually the way to make read-only variables is possible via python's property function/decorator. the answer of inv is an example of a custom usage of that. property is more general-use than that, though, a good analysis of how it works is on Shalabh Chaturvedi's Python Attributes and Methods.Riggall
IMHO, enforcing constancy is "not pythonic". In Python 2.7 you can even write True=False, and then (2+2==4)==True returns False.Jalousie
As other answers suggests there is no way or no need to declare constants. But you may read this PEP about conventions. e.g. THIS_IS_A_CONSTANTFragile
@osa: You can't do that in python 3 - SyntaxError: can't assign to keyword. This seems like a Good Thing.Amice
Surprised this hasn't been mentioned until now, but Enums would seem like a good way to define enumerated constants.Sattler
Now you can use "final" from python.org/dev/peps/pep-0591 or literals from python.org/dev/peps/pep-0586Coadjutor
"final" is a typing decorator, it does nothing to make a value immutable at runtime. If you want an immutable, the Python equivalent of a const is an enumFlemming
S
1364

You cannot declare a variable or value as constant in Python.


To indicate to programmers that a variable is a constant, one usually writes it in upper case:

CONST_NAME = "Name"

To raise exceptions when constants are changed, see Constants in Python by Alex Martelli. Note that this is not commonly used in practice.


As of Python 3.8, there's a typing.Final variable annotation that will tell static type checkers (like mypy) that your variable shouldn't be reassigned. This is the closest equivalent to Java's final. However, it does not actually prevent reassignment:

from typing import Final

a: Final[int] = 1

# Executes fine, but mypy will report an error if you run mypy on this:
a = 2
Sachs answered 21/4, 2010 at 12:21 Comment(6)
In emacs mypy does not give any notation for :Final reassignments. Should I make any configuration setup for it?Product
Advice for beginning programmers: look up why immutability is a code quality mechanism. For senior programmers who think the lack of constant in Python is no problem, do the same.Enfleurage
using javascript for some time and looking for Enum based approach, I found yoni's answer below helpful stackoverflow.com/a/59935007Philately
Note that back in 2010, this answer made sense, but ever since Python 3.4's release in 2014, the answer is "you want to use enum for this", which lets you set an immutable with optional value (because there are two kinds of constants: (1) ones used as a stable key, in which the actual value is irrelevant: the const itself is the value, and (2) ones used for store fixed values)Flemming
@ArnaudMeuret - Immutability is one reason I believe Haskell is the greatest programming language ever created mmhaskell.com/blog/2017/1/9/immutability-is-awesome ; unfortunately, it's often difficult to get permission from your boss to use Haskell...Indoors
@ShawnEary it's not just Haskell, but I agree that immutability is importantStamper
F
439

There's no const keyword as in other languages, however it is possible to create a Property that has a "getter function" to read the data, but no "setter function" to re-write the data. This essentially protects the identifier from being changed.

Here is an alternative implementation using class property:

Note that the code is far from easy for a reader wondering about constants. See explanation below.

def constant(f):
    def fset(self, value):
        raise TypeError
    def fget(self):
        return f()
    return property(fget, fset)

class _Const(object):
    @constant
    def FOO():
        return 0xBAADFACE
    @constant
    def BAR():
        return 0xDEADBEEF

CONST = _Const()

print(hex(CONST.FOO))  # -> '0xbaadfaceL'

CONST.FOO = 0
##Traceback (most recent call last):
##  File "example1.py", line 22, in <module>
##    CONST.FOO = 0
##  File "example1.py", line 5, in fset
##    raise TypeError
##TypeError

Code Explanation:

  1. Define a function constant that takes an expression, and uses it to construct a "getter" - a function that solely returns the value of the expression.
  2. The setter function raises a TypeError so it's read-only
  3. Use the constant function we just created as a decoration to quickly define read-only properties.

And in some other more old-fashioned way:

(The code is quite tricky, more explanations below)

class _Const(object):
    def FOO():
        def fset(self, value):
            raise TypeError
        def fget(self):
            return 0xBAADFACE
        return property(**locals())
    FOO = FOO()  # Define property.

CONST = _Const()

print(hex(CONST.FOO))  # -> '0xbaadfaceL'

CONST.FOO = 0
##Traceback (most recent call last):
##  File "example2.py", line 16, in <module>
##    CONST.FOO = 0
##  File "example2.py", line 6, in fset
##    raise TypeError
##TypeError
  1. To define the identifier FOO, firs define two functions (fset, fget - the names are at my choice).
  2. Then use the built-in property function to construct an object that can be "set" or "get".
  3. Note hat the property function's first two parameters are named fset and fget.
  4. Use the fact that we chose these very names for our own getter & setter and create a keyword-dictionary using the ** (double asterisk) applied to all the local definitions of that scope to pass parameters to the property function
Fontes answered 22/4, 2010 at 3:39 Comment(3)
What should I do if I need a constant uuid uuuid.uuid4().hex? The property will prevent the uuid function from changing, but the value will change.Rendarender
If you want a collection of read-only constructs under a common name, isn't a namedtuple a simpler implementation?Pyatt
This is awesome and seamless integrable thanks to the class, one can create different set of constants, like ERRORS = _Errors() etc.Pantalets
T
161

In Python instead of language enforcing something, people use naming conventions e.g __method for private methods and using _method for protected methods.

So in same manner you can simply declare the constant as all caps, e.g.:

MY_CONSTANT = "one"

If you want that this constant never changes, you can hook into attribute access and do tricks, but a simpler approach is to declare a function:

def MY_CONSTANT():
    return "one"

Only problem is everywhere you will have to do MY_CONSTANT(), but again MY_CONSTANT = "one" is the correct way in Python (usually).


You can also use namedtuple() to create constants:

>>> from collections import namedtuple
>>> Constants = namedtuple('Constants', ['pi', 'e'])
>>> constants = Constants(3.14, 2.718)
>>> constants.pi
3.14
>>> constants.pi = 3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
Thermometry answered 21/4, 2010 at 13:6 Comment(3)
Using def MY_CONSTANT(): return "one" doesn't prevent the method reference from being reassigned, right? Isn't this is exactly how duck typing works?Ainsworth
"If you want that this constant never changes" -- this is a default property of a constant 😬Bangui
constants.pi = 3 fails, but constants = Constants(3, 2) doesn't.Perpetual
E
107

I've recently found a very succinct update to this which automatically raises meaningful error messages and prevents access via __dict__:

class CONST(object):
    __slots__ = ()
    FOO = 1234

CONST = CONST()

# ----------

print(CONST.FOO)    # 1234

CONST.FOO = 4321              # AttributeError: 'CONST' object attribute 'FOO' is read-only
CONST.__dict__['FOO'] = 4321  # AttributeError: 'CONST' object has no attribute '__dict__'
CONST.BAR = 5678              # AttributeError: 'CONST' object has no attribute 'BAR'

We define over ourselves as to make ourselves an instance and then use slots to ensure that no additional attributes can be added. This also removes the __dict__ access route. Of course, the whole object can still be redefined.

Edit - Original solution

I'm probably missing a trick here, but this seems to work for me:

class CONST(object):
    FOO = 1234

    def __setattr__(self, *_):
        pass

CONST = CONST()

#----------

print CONST.FOO    # 1234

CONST.FOO = 4321
CONST.BAR = 5678

print CONST.FOO    # Still 1234!
print CONST.BAR    # Oops AttributeError

Creating the instance allows the magic __setattr__ method to kick in and intercept attempts to set the FOO variable. You could throw an exception here if you wanted to. Instantiating the instance over the class name prevents access directly via the class.

It's a total pain for one value, but you could attach lots to your CONST object. Having an upper class, class name also seems a bit grotty, but I think it's quite succinct overall.

Elga answered 24/4, 2014 at 16:0 Comment(0)
R
52

Python doesn't have constants.

Perhaps the easiest alternative is to define a function for it:

def MY_CONSTANT():
    return 42

MY_CONSTANT() now has all the functionality of a constant (plus some annoying braces).

Raeraeann answered 8/9, 2017 at 22:1 Comment(7)
this is the simplest answer, although it should be noted it has some overhead and will not stop idiots modifying the return value. It will just prevent the code further down the line changing the sourceBiannulate
@Biannulate modifying the return value? Do you mean editing the source? But from this you're not protected even in C++, where constants (like constexpr) are real hard constants.Milomilon
@Milomilon what I meant was that as python has no constexpr, it would not stop the value being edited after it is returned to an outer context. Nothing has been done to 42 to enforce frozen state in this example.Biannulate
It is easy in this case to set new value for MY_CONSTANT, like MY_CONSTANT = 43Tenorite
You can re declare functions in python, so this doesn't actually solve anythingGluteal
um, functions aren’t constant in python, they’re just variables like the others. Next line after that, one can write MY_CONSTANT=43 or MY_CONSTANT=lambda: 43, and the value will be modifiedHaber
Wow. With this answer, you get the worst of both worlds! You need (), ans it's still not a constant. def MY_CONSTANT(): return "whatever"Chelsea
E
34

Properties are one way to create constants. You can do it by declaring a getter property, but ignoring the setter. For example:

class MyFinalProperty(object):

    @property
    def name(self):
        return "John"

You can have a look at an article I've written to find more ways to use Python properties.

Electrolier answered 12/7, 2017 at 22:51 Comment(1)
Nice. Your link is broken, though.Chelsea
A
21

In addition to the two top answers (just use variables with UPPERCASE names, or use properties to make the values read-only), I want to mention that it's possible to use metaclasses in order to implement named constants. I provide a very simple solution using metaclasses at GitHub which may be helpful if you want the values to be more informative about their type/name:

>>> from named_constants import Constants
>>> class Colors(Constants):
...     black = 0
...     red = 1
...     white = 15
...
>>> c = Colors.black
>>> c == 0
True
>>> c
Colors.black
>>> c.name()
'black'
>>> Colors(0) is c
True

This is slightly more advanced Python, but still very easy to use and handy. (The module has some more features, including constants being read-only, see its README.)

There are similar solutions floating around in various repositories, but to the best of my knowledge they either lack one of the fundamental features that I would expect from constants (like being constant, or being of arbitrary type), or they have esoteric features added that make them less generally applicable. But YMMV, I would be grateful for feedback. :-)

Adaurd answered 10/10, 2013 at 21:25 Comment(0)
E
19

Edit: Added sample code for Python 3

Note: this other answer looks like it provides a much more complete implementation similar to the following (with more features).

First, make a metaclass:

class MetaConst(type):
    def __getattr__(cls, key):
        return cls[key]

    def __setattr__(cls, key, value):
        raise TypeError

This prevents statics properties from being changed. Then make another class that uses that metaclass:

class Const(object):
    __metaclass__ = MetaConst

    def __getattr__(self, name):
        return self[name]

    def __setattr__(self, name, value):
        raise TypeError

Or, if you're using Python 3:

class Const(object, metaclass=MetaConst):
    def __getattr__(self, name):
        return self[name]

    def __setattr__(self, name, value):
        raise TypeError

This should prevent instance props from being changed. To use it, inherit:

class MyConst(Const):
    A = 1
    B = 2

Now the props, accessed directly or via an instance, should be constant:

MyConst.A
# 1
my_const = MyConst()
my_const.A
# 1

MyConst.A = 'changed'
# TypeError
my_const.A = 'changed'
# TypeError

Here's an example of above in action. Here's another example for Python 3.

Exaltation answered 3/10, 2015 at 0:28 Comment(0)
G
18

PEP 591 has the 'final' qualifier. Enforcement is down to the type checker.

So you can do:

MY_CONSTANT: Final = 12407

Note: Final keyword is only applicable for Python 3.8 version

Granadilla answered 14/10, 2019 at 20:9 Comment(2)
The Final type is generic and if you're already type checking to enforce this, they'd probably get upset about the untyped usage in your example. I believe that the correct example of this usage should be: MY_CONSTANT: Final[int] = 12407Lundell
@DaveBirch According to that PEP the original statement should be OK: "The typechecker should apply its usual type inference mechanisms to determine the type of ID (here, likely, int)."Animated
O
15
from enum import Enum
class StringConsts(str,Enum):
    ONE='one'
    TWO='two'

print(f'Truth is  {StringConsts.ONE=="one"}') #Truth is True
StringConsts.ONE="one" #Error: Cannot reassign

This mixin of Enum and str gives you the power of not having to reimplement setattr (through Enum) and comparison to other str objects (through str).

This might deprecate http://code.activestate.com/recipes/65207-constants-in-python/?in=user-97991 completely.

Operable answered 27/1, 2020 at 16:18 Comment(0)
A
15

I declare constant values using frozen data class like this:

from dataclasses import dataclass

@dataclass(frozen=True)
class _Const:
    SOME_STRING = 'some_string'
    SOME_INT = 5
    
Const = _Const()

# In another file import Const and try
print(Const.SOME_STRING)  # ITS OK!
Const.SOME_INT = 6  # dataclasses.FrozenInstanceError: cannot assign to field 'SOME_INT'
Alphitomancy answered 4/2, 2022 at 7:10 Comment(0)
Z
12

You can use a namedtuple as a workaround to effectively create a constant that works the same way as a static final variable in Java (a Java "constant"). As workarounds go, it's sort of elegant. (A more elegant approach would be to simply improve the Python language --- what sort of language lets you redefine math.pi? -- but I digress.)

(As I write this, I realize another answer to this question mentioned namedtuple, but I'll continue here because I'll show a syntax that more closely parallels what you'd expect in Java, as there is no need to create a named type as namedtuple forces you to do.)

Following your example, you'll remember that in Java we must define the constant inside some class; because you didn't mention a class name, let's call it Foo. Here's the Java class:

public class Foo {
  public static final String CONST_NAME = "Name";
}

Here's the equivalent Python.

from collections import namedtuple
Foo = namedtuple('_Foo', 'CONST_NAME')('Name')

The key point I want to add here is that you don't need a separate Foo type (an "anonymous named tuple" would be nice, even though that sounds like an oxymoron), so we name our namedtuple _Foo so that hopefully it won't escape to importing modules.

The second point here is that we immediately create an instance of the nametuple, calling it Foo; there's no need to do this in a separate step (unless you want to). Now you can do what you can do in Java:

>>> Foo.CONST_NAME
'Name'

But you can't assign to it:

>>> Foo.CONST_NAME = 'bar'
…
AttributeError: can't set attribute

Acknowledgement: I thought I invented the namedtuple approach, but then I see that someone else gave a similar (although less compact) answer. Then I also noticed What are "named tuples" in Python?, which points out that sys.version_info is now a namedtuple, so perhaps the Python standard library already came up with this idea much earlier.

Note that unfortunately (this still being Python), you can erase the entire Foo assignment altogether:

>>> Foo = 'bar'

(facepalm)

But at least we're preventing the Foo.CONST_NAME value from being changed, and that's better than nothing. Good luck.

Zaccaria answered 13/10, 2016 at 19:44 Comment(0)
F
9

Here is an implementation of a "Constants" class, which creates instances with read-only (constant) attributes. E.g. can use Nums.PI to get a value that has been initialized as 3.14159, and Nums.PI = 22 raises an exception.

# ---------- Constants.py ----------
class Constants(object):
    """
    Create objects with read-only (constant) attributes.
    Example:
        Nums = Constants(ONE=1, PI=3.14159, DefaultWidth=100.0)
        print 10 + Nums.PI
        print '----- Following line is deliberate ValueError -----'
        Nums.PI = 22
    """

    def __init__(self, *args, **kwargs):
        self._d = dict(*args, **kwargs)

    def __iter__(self):
        return iter(self._d)

    def __len__(self):
        return len(self._d)

    # NOTE: This is only called if self lacks the attribute.
    # So it does not interfere with get of 'self._d', etc.
    def __getattr__(self, name):
        return self._d[name]

    # ASSUMES '_..' attribute is OK to set. Need this to initialize 'self._d', etc.
    #If use as keys, they won't be constant.
    def __setattr__(self, name, value):
        if (name[0] == '_'):
            super(Constants, self).__setattr__(name, value)
        else:
            raise ValueError("setattr while locked", self)

if (__name__ == "__main__"):
    # Usage example.
    Nums = Constants(ONE=1, PI=3.14159, DefaultWidth=100.0)
    print 10 + Nums.PI
    print '----- Following line is deliberate ValueError -----'
    Nums.PI = 22

Thanks to @MikeGraham 's FrozenDict, which I used as a starting point. Changed, so instead of Nums['ONE'] the usage syntax is Nums.ONE.

And thanks to @Raufio's answer, for idea to override __ setattr __.

Or for an implementation with more functionality, see @Hans_meine 's named_constants at GitHub

Fixed answered 11/12, 2013 at 0:19 Comment(1)
Python is a language of consenting adults. There is no protection against something like this. Nums._d['PI'] = 22 The language itself doesn't provide any way to mark things as non-mutables, I believe.Tumbler
L
9

A tuple technically qualifies as a constant, as a tuple will raise an error if you try to change one of its values. If you want to declare a tuple with one value, then place a comma after its only value, like this:

my_tuple = (0 """Or any other value""",)

To check this variable's value, use something similar to this:

if my_tuple[0] == 0:
    #Code goes here

If you attempt to change this value, an error will be raised.

Lateshalatest answered 9/5, 2016 at 21:34 Comment(0)
P
8

We can create a descriptor object.

class Constant:
  def __init__(self,value=None):
    self.value = value
  def __get__(self,instance,owner):
    return self.value
  def __set__(self,instance,value):
    raise ValueError("You can't change a constant")

1) If we wanted to work with constants at the instance level then:

class A:
  NULL = Constant()
  NUM = Constant(0xFF)

class B:
  NAME = Constant('bar')
  LISTA = Constant([0,1,'INFINITY'])

>>> obj=A()
>>> print(obj.NUM)  #=> 255
>>> obj.NUM =100

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: You can't change a constant

2) if we wanted to create constants only at the class level, we could use a metaclass that serves as a container for our constants (our descriptor objects); all the classes that descend will inherit our constants (our descriptor objects) without any risk that can be modified.

# metaclass of my class Foo
class FooMeta(type): pass

# class Foo
class Foo(metaclass=FooMeta): pass

# I create constants in my metaclass
FooMeta.NUM = Constant(0xff)
FooMeta.NAME = Constant('FOO')

>>> Foo.NUM   #=> 255
>>> Foo.NAME  #=> 'FOO'
>>> Foo.NUM = 0 #=> ValueError: You can't change a constant

If I create a subclass of Foo, this class will inherit the constant without the possibility of modifying them

class Bar(Foo): pass

>>> Bar.NUM  #=> 255
>>> Bar.NUM = 0  #=> ValueError: You can't change a constant
Pontine answered 12/5, 2017 at 15:57 Comment(0)
P
8

Here it is a collection of idioms that I created as an attempt to improve some of the already available answers.

I know the use of constant is not pythonic, and you should not do this at home!

However, Python is such a dynamic language! This forum shows how it is possible the creation of constructs that looks and feels like constants. This answer has as the primary purpose to explore what can be expressed by the language.

Please do not be too harsh with me :-).

For more details I wrote a accompaniment blog about these idioms.

In this post, I will call a constant variable to a constant reference to values (immutable or otherwise). Moreover, I say that a variable has a frozen value when it references a mutable object that a client-code cannot update its value(s).

A space of constants (SpaceConstants)

This idiom creates what looks like a namespace of constant variables (a.k.a. SpaceConstants). It is a modification of a code snippet by Alex Martelli to avoid the use of module objects. In particular, this modification uses what I call a class factory because within SpaceConstants function, a class called SpaceConstants is defined, and an instance of it is returned.

I explored the use of class factory to implement a policy-based design look-alike in Python in stackoverflow and also in a blogpost.

def SpaceConstants():
    def setattr(self, name, value):
        if hasattr(self, name):
            raise AttributeError(
                "Cannot reassign members"
            )
        self.__dict__[name] = value
    cls = type('SpaceConstants', (), {
        '__setattr__': setattr
    })
    return cls()

sc = SpaceConstants()

print(sc.x) # raise "AttributeError: 'SpaceConstants' object has no attribute 'x'"
sc.x = 2 # bind attribute x
print(sc.x) # print "2"
sc.x = 3 # raise "AttributeError: Cannot reassign members"
sc.y = {'name': 'y', 'value': 2} # bind attribute y
print(sc.y) # print "{'name': 'y', 'value': 2}"
sc.y['name'] = 'yprime' # mutable object can be changed
print(sc.y) # print "{'name': 'yprime', 'value': 2}"
sc.y = {} # raise "AttributeError: Cannot reassign members"

A space of frozen values (SpaceFrozenValues)

This next idiom is a modification of the SpaceConstants in where referenced mutable objects are frozen. This implementation exploits what I call shared closure between setattr and getattr functions. The value of the mutable object is copied and referenced by variable cache define inside of the function shared closure. It forms what I call a closure protected copy of a mutable object.

You must be careful in using this idiom because getattr return the value of cache by doing a deep copy. This operation could have a significant performance impact on large objects!

from copy import deepcopy

def SpaceFrozenValues():
    cache = {}
    def setattr(self, name, value):
        nonlocal cache
        if name in cache:
            raise AttributeError(
                "Cannot reassign members"
            )
        cache[name] = deepcopy(value)
    def getattr(self, name):
        nonlocal cache
        if name not in cache:
            raise AttributeError(
                "Object has no attribute '{}'".format(name)
            )
        return deepcopy(cache[name])
    cls = type('SpaceFrozenValues', (),{
        '__getattr__': getattr,
        '__setattr__': setattr
    })
    return cls()

fv = SpaceFrozenValues()
print(fv.x) # AttributeError: Object has no attribute 'x'
fv.x = 2 # bind attribute x
print(fv.x) # print "2"
fv.x = 3 # raise "AttributeError: Cannot reassign members"
fv.y = {'name': 'y', 'value': 2} # bind attribute y
print(fv.y) # print "{'name': 'y', 'value': 2}"
fv.y['name'] = 'yprime' # you can try to change mutable objects
print(fv.y) # print "{'name': 'y', 'value': 2}"
fv.y = {} # raise "AttributeError: Cannot reassign members"

A constant space (ConstantSpace)

This idiom is an immutable namespace of constant variables or ConstantSpace. It is a combination of awesomely simple Jon Betts' answer in stackoverflow with a class factory.

def ConstantSpace(**args):
    args['__slots__'] = ()
    cls = type('ConstantSpace', (), args)
    return cls()

cs = ConstantSpace(
    x = 2,
    y = {'name': 'y', 'value': 2}
)

print(cs.x) # print "2"
cs.x = 3 # raise "AttributeError: 'ConstantSpace' object attribute 'x' is read-only"
print(cs.y) # print "{'name': 'y', 'value': 2}"
cs.y['name'] = 'yprime' # mutable object can be changed
print(cs.y) # print "{'name': 'yprime', 'value': 2}"
cs.y = {} # raise "AttributeError: 'ConstantSpace' object attribute 'x' is read-only"
cs.z = 3 # raise "AttributeError: 'ConstantSpace' object has no attribute 'z'"

A frozen space (FrozenSpace)

This idiom is an immutable namespace of frozen variables or FrozenSpace. It is derived from the previous pattern by making each variable a protected property by closure of the generated FrozenSpace class.

from copy import deepcopy

def FreezeProperty(value):
    cache = deepcopy(value)
    return property(
        lambda self: deepcopy(cache)
    )

def FrozenSpace(**args):
    args = {k: FreezeProperty(v) for k, v in args.items()}
    args['__slots__'] = ()
    cls = type('FrozenSpace', (), args)
    return cls()

fs = FrozenSpace(
    x = 2,
    y = {'name': 'y', 'value': 2}
)

print(fs.x) # print "2"
fs.x = 3 # raise "AttributeError: 'FrozenSpace' object attribute 'x' is read-only"
print(fs.y) # print "{'name': 'y', 'value': 2}"
fs.y['name'] = 'yprime' # try to change mutable object
print(fs.y) # print "{'name': 'y', 'value': 2}"
fs.y = {} # raise "AttributeError: 'FrozenSpace' object attribute 'x' is read-only"
fs.z = 3 # raise "AttributeError: 'FrozenSpace' object has no attribute 'z'"
Pectoralis answered 17/8, 2019 at 4:23 Comment(0)
I
7

I would make a class that overrides the __setattr__ method of the base object class and wrap my constants with that, note that I'm using python 2.7:

class const(object):
    def __init__(self, val):
        super(const, self).__setattr__("value", val)
    def __setattr__(self, name, val):
        raise ValueError("Trying to change a constant value", self)

To wrap a string:

>>> constObj = const("Try to change me")
>>> constObj.value
'Try to change me'
>>> constObj.value = "Changed"
Traceback (most recent call last):
   ...
ValueError: Trying to change a constant value
>>> constObj2 = const(" or not")
>>> mutableObj = constObj.value + constObj2.value
>>> mutableObj #just a string
'Try to change me or not'

It's pretty simple, but if you want to use your constants the same as you would a non-constant object (without using constObj.value), it will be a bit more intensive. It's possible that this could cause problems, so it might be best to keep the .value to show and know that you are doing operations with constants (maybe not the most 'pythonic' way though).

Icarus answered 10/6, 2013 at 0:9 Comment(0)
R
7

Unfortunately the Python has no constants so yet and it is shame. ES6 already added support constants to JavaScript (https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/const) since it is a very useful thing in any programming language. As answered in other answers in Python community use the convention - user uppercase variable as constants, but it does not protect against arbitrary errors in code. If you like, you may be found useful a single-file solution as next (see docstrings how use it).

file constants.py

import collections


__all__ = ('const', )


class Constant(object):
    """
    Implementation strict constants in Python 3.

    A constant can be set up, but can not be changed or deleted.
    Value of constant may any immutable type, as well as list or set.
    Besides if value of a constant is list or set, it will be converted in an immutable type as next:
        list -> tuple
        set -> frozenset
    Dict as value of a constant has no support.

    >>> const = Constant()
    >>> del const.temp
    Traceback (most recent call last):
    NameError: name 'temp' is not defined
    >>> const.temp = 1
    >>> const.temp = 88
    Traceback (most recent call last):
        ...
    TypeError: Constanst can not be changed
    >>> del const.temp
    Traceback (most recent call last):
        ...
    TypeError: Constanst can not be deleted
    >>> const.I = ['a', 1, 1.2]
    >>> print(const.I)
    ('a', 1, 1.2)
    >>> const.F = {1.2}
    >>> print(const.F)
    frozenset([1.2])
    >>> const.D = dict()
    Traceback (most recent call last):
        ...
    TypeError: dict can not be used as constant
    >>> del const.UNDEFINED
    Traceback (most recent call last):
        ...
    NameError: name 'UNDEFINED' is not defined
    >>> const()
    {'I': ('a', 1, 1.2), 'temp': 1, 'F': frozenset([1.2])}
    """

    def __setattr__(self, name, value):
        """Declaration a constant with value. If mutable - it will be converted to immutable, if possible.
        If the constant already exists, then made prevent againt change it."""

        if name in self.__dict__:
            raise TypeError('Constanst can not be changed')

        if not isinstance(value, collections.Hashable):
            if isinstance(value, list):
                value = tuple(value)
            elif isinstance(value, set):
                value = frozenset(value)
            elif isinstance(value, dict):
                raise TypeError('dict can not be used as constant')
            else:
                raise ValueError('Muttable or custom type is not supported')
        self.__dict__[name] = value

    def __delattr__(self, name):
        """Deny against deleting a declared constant."""

        if name in self.__dict__:
            raise TypeError('Constanst can not be deleted')
        raise NameError("name '%s' is not defined" % name)

    def __call__(self):
        """Return all constans."""

        return self.__dict__


const = Constant()


if __name__ == '__main__':
    import doctest
    doctest.testmod()

If this is not enough, see full testcase for it.

import decimal
import uuid
import datetime
import unittest

from ..constants import Constant


class TestConstant(unittest.TestCase):
    """
    Test for implementation constants in the Python
    """

    def setUp(self):

        self.const = Constant()

    def tearDown(self):

        del self.const

    def test_create_constant_with_different_variants_of_name(self):

        self.const.CONSTANT = 1
        self.assertEqual(self.const.CONSTANT, 1)
        self.const.Constant = 2
        self.assertEqual(self.const.Constant, 2)
        self.const.ConStAnT = 3
        self.assertEqual(self.const.ConStAnT, 3)
        self.const.constant = 4
        self.assertEqual(self.const.constant, 4)
        self.const.co_ns_ta_nt = 5
        self.assertEqual(self.const.co_ns_ta_nt, 5)
        self.const.constant1111 = 6
        self.assertEqual(self.const.constant1111, 6)

    def test_create_and_change_integer_constant(self):

        self.const.INT = 1234
        self.assertEqual(self.const.INT, 1234)
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.INT = .211

    def test_create_and_change_float_constant(self):

        self.const.FLOAT = .1234
        self.assertEqual(self.const.FLOAT, .1234)
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.FLOAT = .211

    def test_create_and_change_list_constant_but_saved_as_tuple(self):

        self.const.LIST = [1, .2, None, True, datetime.date.today(), [], {}]
        self.assertEqual(self.const.LIST, (1, .2, None, True, datetime.date.today(), [], {}))

        self.assertTrue(isinstance(self.const.LIST, tuple))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.LIST = .211

    def test_create_and_change_none_constant(self):

        self.const.NONE = None
        self.assertEqual(self.const.NONE, None)
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.NONE = .211

    def test_create_and_change_boolean_constant(self):

        self.const.BOOLEAN = True
        self.assertEqual(self.const.BOOLEAN, True)
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.BOOLEAN = False

    def test_create_and_change_string_constant(self):

        self.const.STRING = "Text"
        self.assertEqual(self.const.STRING, "Text")

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.STRING += '...'

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.STRING = 'TEst1'

    def test_create_dict_constant(self):

        with self.assertRaisesRegexp(TypeError, 'dict can not be used as constant'):
            self.const.DICT = {}

    def test_create_and_change_tuple_constant(self):

        self.const.TUPLE = (1, .2, None, True, datetime.date.today(), [], {})
        self.assertEqual(self.const.TUPLE, (1, .2, None, True, datetime.date.today(), [], {}))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.TUPLE = 'TEst1'

    def test_create_and_change_set_constant(self):

        self.const.SET = {1, .2, None, True, datetime.date.today()}
        self.assertEqual(self.const.SET, {1, .2, None, True, datetime.date.today()})

        self.assertTrue(isinstance(self.const.SET, frozenset))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.SET = 3212

    def test_create_and_change_frozenset_constant(self):

        self.const.FROZENSET = frozenset({1, .2, None, True, datetime.date.today()})
        self.assertEqual(self.const.FROZENSET, frozenset({1, .2, None, True, datetime.date.today()}))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.FROZENSET = True

    def test_create_and_change_date_constant(self):

        self.const.DATE = datetime.date(1111, 11, 11)
        self.assertEqual(self.const.DATE, datetime.date(1111, 11, 11))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.DATE = True

    def test_create_and_change_datetime_constant(self):

        self.const.DATETIME = datetime.datetime(2000, 10, 10, 10, 10)
        self.assertEqual(self.const.DATETIME, datetime.datetime(2000, 10, 10, 10, 10))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.DATETIME = None

    def test_create_and_change_decimal_constant(self):

        self.const.DECIMAL = decimal.Decimal(13123.12312312321)
        self.assertEqual(self.const.DECIMAL, decimal.Decimal(13123.12312312321))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.DECIMAL = None

    def test_create_and_change_timedelta_constant(self):

        self.const.TIMEDELTA = datetime.timedelta(days=45)
        self.assertEqual(self.const.TIMEDELTA, datetime.timedelta(days=45))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.TIMEDELTA = 1

    def test_create_and_change_uuid_constant(self):

        value = uuid.uuid4()
        self.const.UUID = value
        self.assertEqual(self.const.UUID, value)

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.UUID = []

    def test_try_delete_defined_const(self):

        self.const.VERSION = '0.0.1'
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be deleted'):
            del self.const.VERSION

    def test_try_delete_undefined_const(self):

        with self.assertRaisesRegexp(NameError, "name 'UNDEFINED' is not defined"):
            del self.const.UNDEFINED

    def test_get_all_defined_constants(self):

        self.assertDictEqual(self.const(), {})

        self.const.A = 1
        self.assertDictEqual(self.const(), {'A': 1})

        self.const.B = "Text"
        self.assertDictEqual(self.const(), {'A': 1, 'B': "Text"})

Advantages: 1. Access to all constants for whole project 2. Strict control for values of constants

Lacks: 1. Not support for custom types and the type 'dict'

Notes:

  1. Tested with Python3.4 and Python3.5 (I am use the 'tox' for it)

  2. Testing environment:

.

$ uname -a
Linux wlysenko-Aspire 3.13.0-37-generic #64-Ubuntu SMP Mon Sep 22 21:28:38 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
Reggy answered 5/12, 2016 at 9:46 Comment(1)
You could improve this slightly by automatically converting dictionaries to named tuplesEgan
D
6

The Pythonic way of declaring "constants" is basically a module level variable:

RED = 1
GREEN = 2
BLUE = 3

And then write your classes or functions. Since constants are almost always integers, and they are also immutable in Python, you have a very little chance of altering it.

Unless, of course, if you explicitly set RED = 2.

Disremember answered 21/4, 2010 at 13:28 Comment(4)
Yes, but blocking the ability to "explicitly set RED = 2" is the entire benefit (in other languages) of being able to declare a variable name to be "constant"!Fixed
Would benefit would you get from blocking that? The most useful thing about const is usually compiler optimizations which isn't really a thing in Python. Want something to be constant? Just don't change it. If you're worrying about someone else changing it, you could just put it outside of their scope, or just realize that, if someone is changing it, that's their problem and they need to deal with it, not you.Robbyrobbyn
@Kevin: "Would benefit would you get...", the benefit of static to have a single storage for the value for all instances of a class? Unless there is a possibility to declare a static/class variable indeed.Jowers
The root issue is that some may see it as a value that is a source of truth, unable to be changed, and use it as the source of truth throughout their code instead of introducing magic values (which I see a lot of in Python) - and others may see it as something they're allowed to change at will. When someone changes a global variable, and you can't tell where it got changed, and the application crashes because RED="blue" instead of "red", you're introducing a totally unnecessary problem that has already been solved so simply and is universally understood.Hotbed
G
6

There is a cleaner way to do this with namedtuple:

from collections import namedtuple


def make_consts(name, **kwargs):
    return namedtuple(name, kwargs.keys())(**kwargs)

Usage Example

CONSTS = make_consts("baz1",
                     foo=1,
                     bar=2)

With this exactly approach you can namespace your constants.

Guadalajara answered 12/9, 2018 at 17:46 Comment(2)
For everyone who's reading this, please, keep in mind that, if you set a mutable object as one of these constants, anyone can alter its internal value. for example, lets bar=[1, 2, 3], then, you could do as follows: CONSTS.bar[1] = 'a' and it won't be rejected. So be careful about this.Integumentary
Instead of this hacky method, which I made just for fun, I recommend using Python's property decorator instead.Integumentary
B
5

Here's a trick if you want constants and don't care their values:

Just define empty classes.

e.g:

class RED: 
    pass
class BLUE: 
    pass
Bombay answered 11/8, 2017 at 7:3 Comment(0)
T
5

There's no perfect way to do this. As I understand it most programmers will just capitalize the identifier, so PI = 3.142 can be readily understood to be a constant.

On the otherhand, if you want something that actually acts like a constant, I'm not sure you'll find it. With anything you do there will always be some way of editing the "constant" so it won't really be a constant. Here's a very simple, dirty example:

def define(name, value):
  if (name + str(id(name))) not in globals():
    globals()[name + str(id(name))] = value

def constant(name):
  return globals()[name + str(id(name))]

define("PI",3.142)

print(constant("PI"))

This looks like it will make a PHP-style constant.

In reality all it takes for someone to change the value is this:

globals()["PI"+str(id("PI"))] = 3.1415

This is the same for all the other solutions you'll find on here - even the clever ones that make a class and redefine the set attribute method - there will always be a way around them. That's just how Python is.

My recommendation is to just avoid all the hassle and just capitalize your identifiers. It wouldn't really be a proper constant but then again nothing would.

Therontheropod answered 23/7, 2018 at 14:2 Comment(0)
D
5

I am trying different ways to create a real constant in Python and perhaps I found the pretty solution.

Example:

Create container for constants

>>> DAYS = Constants(
...     MON=0,
...     TUE=1,
...     WED=2,
...     THU=3,
...     FRI=4,
...     SAT=5,
...     SUN=6
... )   

Get value from container

>>> DAYS.MON
0
>>> DAYS['MON']
0  

Represent with pure python data structures

>>> list(DAYS)
['WED', 'SUN', 'FRI', 'THU', 'MON', 'TUE', 'SAT']
>>> dict(DAYS)
{'WED': 2, 'SUN': 6, 'FRI': 4, 'THU': 3, 'MON': 0, 'TUE': 1, 'SAT': 5}

All constants are immutable

>>> DAYS.MON = 7
...
AttributeError: Immutable attribute

>>> del DAYS.MON 
...
AttributeError: Immutable attribute

Autocomplete only for constants

>>> dir(DAYS)
['FRI', 'MON', 'SAT', 'SUN', 'THU', 'TUE', 'WED']

Sorting like list.sort

>>> DAYS.sort(key=lambda (k, v): v, reverse=True)
>>> list(DAYS)
['SUN', 'SAT', 'FRI', 'THU', 'WED', 'TUE', 'MON']

Copability with python2 and python3

Simple container for constants

from collections import OrderedDict
from copy import deepcopy

class Constants(object):
    """Container of constant"""

    __slots__ = ('__dict__')

    def __init__(self, **kwargs):

        if list(filter(lambda x: not x.isupper(), kwargs)):
            raise AttributeError('Constant name should be uppercase.')

        super(Constants, self).__setattr__(
            '__dict__',
            OrderedDict(map(lambda x: (x[0], deepcopy(x[1])), kwargs.items()))
        )

    def sort(self, key=None, reverse=False):
        super(Constants, self).__setattr__(
            '__dict__',
            OrderedDict(sorted(self.__dict__.items(), key=key, reverse=reverse))
        )

    def __getitem__(self, name):
        return self.__dict__[name]

    def __len__(self):
        return  len(self.__dict__)

    def __iter__(self):
        for name in self.__dict__:
            yield name

    def keys(self):
        return list(self)

    def __str__(self):
        return str(list(self))

    def __repr__(self):
        return '<%s: %s>' % (self.__class__.__name__, str(self.__dict__))

    def __dir__(self):
        return list(self)

    def __setattr__(self, name, value):
        raise AttributeError("Immutable attribute")

    def __delattr__(*_):
        raise AttributeError("Immutable attribute")

Diametral answered 6/4, 2020 at 22:2 Comment(0)
U
4

Python dictionaries are mutable, so they don't seem like a good way to declare constants:

>>> constants = {"foo":1, "bar":2}
>>> print constants
{'foo': 1, 'bar': 2}
>>> constants["bar"] = 3
>>> print constants
{'foo': 1, 'bar': 3}
Utilitarianism answered 30/6, 2011 at 7:36 Comment(0)
W
4

In python, a constant is simply a variable with a name in all capitals, with words separated by the underscore character,

e.g

DAYS_IN_WEEK = 7

The value is mutable, as in you can change it. But given the rules for the name tell you is a constant, why would you? I mean, it is your program after all!

This is the approach taken throughout python. There is no private keyword for the same reason. Prefix the name with an underscore and you know it is intended to be private. Code can break the rule....just as a programmer could remove the private keyword anyway.

Python could have added a const keyword... but a programmer could remove keyword and then change the constant if they want to, but why do that? If you want to break the rule, you could change the rule anyway. But why bother to break the rule if the name makes the intention clear?

Maybe there is some unit test where it makes sense to apply a change to value? To see what happens for an 8 day week even though in the real world the number of days in the week cannot be changed. If the language stopped you making an exception if there is just this one case you need to break the rule...you would then have to stop declaring it as a constant, even though it still is a constant in the application, and there is just this one test case that sees what happens if it is changed.

The all upper case name tells you it is intended to be a constant. That is what is important. Not a language forcing constraints on code you have the power to change anyway.

That is the philosophy of python.

Walleyed answered 30/4, 2018 at 12:14 Comment(0)
G
4

(This paragraph was meant to be a comment on those answers here and there, which mentioned namedtuple, but it is getting too long to be fit into a comment, so, here it goes.)

The namedtuple approach mentioned above is definitely innovative. For the sake of completeness, though, at the end of the NamedTuple section of its official documentation, it reads:

enumerated constants can be implemented with named tuples, but it is simpler and more efficient to use a simple class declaration:

class Status:
    open, pending, closed = range(3)

In other words, the official documentation kind of prefers to use a practical way, rather than actually implementing the read-only behavior. I guess it becomes yet another example of Zen of Python:

Simple is better than complex.

practicality beats purity.

Gingerich answered 6/9, 2018 at 0:5 Comment(0)
P
4

Maybe pconst library will help you (github).

$ pip install pconst

from pconst import const
const.APPLE_PRICE = 100
const.APPLE_PRICE = 200

[Out] Constant value of "APPLE_PRICE" is not editable.

Pentlandite answered 29/10, 2018 at 12:24 Comment(0)
G
2

You can use StringVar or IntVar, etc, your constant is const_val

val = 'Stackoverflow'
const_val = StringVar(val)
const.trace('w', reverse)

def reverse(*args):
    const_val.set(val)
Germiston answered 30/9, 2017 at 8:53 Comment(0)
B
2

You can do it with collections.namedtuple and itertools:

import collections
import itertools
def Constants(Name, *Args, **Kwargs):
  t = collections.namedtuple(Name, itertools.chain(Args, Kwargs.keys()))
  return t(*itertools.chain(Args, Kwargs.values()))

>>> myConstants = Constants('MyConstants', 'One', 'Two', Three = 'Four')
>>> print myConstants.One
One
>>> print myConstants.Two
Two
>>> print myConstants.Three
Four
>>> myConstants.One = 'Two'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
Beaumont answered 27/11, 2017 at 20:20 Comment(0)
I
2

In Python, constants do not exist, but you can indicate that a variable is a constant and must not be changed by adding CONST_ to the start of the variable name and stating that it is a constant in a comment:

myVariable = 0
CONST_daysInWeek = 7    # This is a constant - do not change its value.   
CONSTANT_daysInMonth = 30 # This is also a constant - do not change this value.

Alternatively, you may create a function that acts like a constant:

def CONST_daysInWeek():
    return 7;
Illdisposed answered 14/12, 2017 at 20:3 Comment(0)
M
2

All of the answers given are essentially of two types:

  1. Create some sort of object for which you can create attributes which cannot be changed once defined.
  2. Use a convention (like writing constants in all UPPERCASE or, for Python 3.8, use the final qualifier to indicate that you intend one or more names to be a constant.

They can be summarized as saying "you cannot do what you ask using Python".

However, there is actually a way to create a module with true constants. The code to do so is rather involved, and I will only give an outline of what is needed to do as it is already available under an open source license.

  1. Use an import hook to enable the creation of a custom module. The versatile code I use for this is found here.
  2. Create a special dict which allows adding items that conform to your chosen pattern (for example, names in all UPPERCASE) only once and prevent such names to have their values changed. For this, you will need to define your own methods such as __setitem__, __delitem__, etc. The code for such a dict (such as found in this file, which is over 250 lines) is approximately 100 lines long.
  3. The dict for a normal Python module cannot be modified. So, when creating the module, you need to execute the code in your special dict first, and then use its content to update the module's dict.
  4. To prevent modifications of the values of the contants from outside of the module (i.e. monkeypatching), you can replace the __class__ of the module by a custom one with the __setattr__ and __delattr__ method redefined.

The documentation about this example can be found here. It probably should be updated to reflect the number of answers given to this question.

Midsection answered 28/2, 2020 at 21:32 Comment(0)
C
2

A better approach to creating constants in Python is to take inspiration from the excellent attrs library, which helps Python programmers create classes without boilerplate. The short-con package does the same for constants by providing a convenience wrapper around attr.make_class. [Disclaimer: I am the author of short-con.]

Values can be declared explicitly via either a dict or kwargs: These examples do the same thing. The constants() function supports all features of the library, and cons() is a helper for simple kwarg-based usage.

from short_con import constants, cons

Pieces = constants('Pieces', dict(king = 0, queen = 9, rook = 5, bishop = 3, knight = 3, pawn = 1))
Pieces = cons('Pieces', king = 0, queen = 9, rook = 5, bishop = 3, knight = 3, pawn = 1)

For situations when the values are the same as (or can be derived from) the attribute names, usage is even more compact. Just supply names as a space-delimited string, list, or tuple.

NAMES = 'KING QUEEN ROOK BISHOP KNIGHT PAWN'
xs = NAMES.split()

Pieces = constants('Pieces', NAMES)      # All of these do the same thing.
Pieces = constants('Pieces', xs)
Pieces = constants('Pieces', tuple(xs))

The name-based usages support a few stylistic conventions: uppercase or lowercase attribute names, along with enum-style values.

The underlying values are directly accessible, unlike constants created by the built-in enum library:

Pieces.QUEEN        # short-con usage
Pieces.QUEEN.value  # enum library usage

And the object is directly iterable and convertible to other collections:

for name, value in Pieces:
    print(name, value)

d = dict(Pieces)
tups = list(Pieces)
Compilation answered 12/9, 2020 at 1:23 Comment(0)
T
2

This is not exactly constant but starting from python 3.7 you may use dataclasses module like below:

from dataclasses import dataclass
from typing import Final

@dataclass(frozen=True)
class A():
    a1:Final = 3

a = A()

a.a1 = 4

---------------------------------------------------------------------------
FrozenInstanceError                       Traceback (most recent call last)
<ipython-input-14-5f7f4efc5bf0> in <module>
----> 1 a.a1 = 4

<string> in __setattr__(self, name, value)

FrozenInstanceError: cannot assign to field 'a1'
Tenorite answered 8/1, 2022 at 15:18 Comment(0)
P
1

In my case, I needed immutable bytearrays for an implementation of a crypto library containing many literal numbers I wanted to ensure were constant.

This answer works but attempted reassignment of bytearray elements does not raise an error.

def const(func):
    '''implement const decorator'''
    def fset(self, val):
        '''attempting to set a const raises `ConstError`'''
        class ConstError(TypeError):
            '''special exception for const reassignment'''
            pass

        raise ConstError

    def fget(self):
        '''get a const'''
        return func()

    return property(fget, fset)


class Consts(object):
    '''contain all constants'''

    @const
    def C1():
        '''reassignment to C1 fails silently'''
        return bytearray.fromhex('deadbeef')

    @const
    def pi():
        '''is immutable'''
        return 3.141592653589793

Constants are immutable, but constant bytearray assignment fails silently:

>>> c = Consts()
>>> c.pi = 6.283185307179586  # (https://en.wikipedia.org/wiki/Tau_(2%CF%80))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "consts.py", line 9, in fset
    raise ConstError
__main__.ConstError
>>> c.C1[0] = 0
>>> c.C1[0]
222
>>> c.C1
bytearray(b'\xde\xad\xbe\xef')

A more powerful, simple, and perhaps even more 'pythonic' approach involves the use of memoryview objects (buffer objects in <= python-2.6).

import sys

PY_VER = sys.version.split()[0].split('.')

if int(PY_VER[0]) == 2:
    if int(PY_VER[1]) < 6:
        raise NotImplementedError
    elif int(PY_VER[1]) == 6:
        memoryview = buffer

class ConstArray(object):
    '''represent a constant bytearray'''
    def __init__(self, init):
        '''
        create a hidden bytearray and expose a memoryview of that bytearray for
        read-only use
        '''
        if int(PY_VER[1]) == 6:
            self.__array = bytearray(init.decode('hex'))
        else:
            self.__array = bytearray.fromhex(init)

        self.array = memoryview(self.__array)

    def __str__(self):
        return str(self.__array)

    def __getitem__(self, *args, **kwargs):
       return self.array.__getitem__(*args, **kwargs)

ConstArray item assignment is a TypeError:

>>> C1 = ConstArray('deadbeef')
>>> C1[0] = 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'ConstArray' object does not support item assignment
>>> C1[0]
222
Pugnacious answered 25/2, 2015 at 16:37 Comment(0)
O
1

I write a util lib for python const: kkconst - pypi support str, int, float, datetime

the const field instance will keep its base type behavior.

For example:

from __future__ import print_function
from kkconst import (
    BaseConst,
    ConstFloatField,
)

class MathConst(BaseConst):
    PI = ConstFloatField(3.1415926, verbose_name=u"Pi")
    E = ConstFloatField(2.7182818284, verbose_name=u"mathematical constant")  # Euler's number"
    GOLDEN_RATIO = ConstFloatField(0.6180339887, verbose_name=u"Golden Ratio")

magic_num = MathConst.GOLDEN_RATIO
assert isinstance(magic_num, ConstFloatField)
assert isinstance(magic_num, float)

print(magic_num)  # 0.6180339887
print(magic_num.verbose_name)  # Golden Ratio

more details usage you can read the pypi url: pypi or github

Otha answered 23/12, 2015 at 10:49 Comment(0)
L
1

You can wrap a constant in a numpy array, flag it write only, and always call it by index zero.

import numpy as np

# declare a constant
CONSTANT = 'hello'

# put constant in numpy and make read only
CONSTANT = np.array([CONSTANT])
CONSTANT.flags.writeable = False
# alternatively: CONSTANT.setflags(write=0)

# call our constant using 0 index    
print 'CONSTANT %s' % CONSTANT[0]

# attempt to modify our constant with try/except
new_value = 'goodbye'
try:
    CONSTANT[0] = new_value
except:
    print "cannot change CONSTANT to '%s' it's value '%s' is immutable" % (
        new_value, CONSTANT[0])

# attempt to modify our constant producing ValueError
CONSTANT[0] = new_value



>>>
CONSTANT hello
cannot change CONSTANT to 'goodbye' it's value 'hello' is immutable
Traceback (most recent call last):
  File "shuffle_test.py", line 15, in <module>
    CONSTANT[0] = new_value
ValueError: assignment destination is read-only

of course this only protects the contents of the numpy, not the variable "CONSTANT" itself; you can still do:

CONSTANT = 'foo'

and CONSTANT would change, however that would quickly throw an TypeError the first time CONSTANT[0] is later called in the script.

although... I suppose if you at some point changed it to

CONSTANT = [1,2,3]

now you wouldn't get the TypeError anymore. hmmmm....

https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.setflags.html

Lusatian answered 1/4, 2017 at 3:33 Comment(0)
W
1

I know this is an old question, but since new solutions are still being added to it, I'd like to make the list of possible solutions even more complete. You could implement constants within instances through attribute access by inheriting from a class like the following:

class ConstantError(Exception):
    pass  # maybe give nice error message

class AllowConstants:
    _constants = None
    _class_constants = None

    def __init__(self):
        self._constants = {}
        if self._class_constants is not None:
            self._constants.update(self._class_constants)

    def constant(self, name, value):
        assert isinstance(name, str)
        assert self._constants is not None, "AllowConstants was not initialized"
        if name in self._constants or name in self.__dict__:
            raise ConstantError(name)
        self._constants[name] = value

    def __getattr__(self, attr):
        if attr in self._constants:
            return self._constants[attr]
        raise AttributeError(attr)

    def __setattr__(self, attr, val):
        if self._constants is None:
            # not finished initialization
            self.__dict__[attr] = val
        else:
            if attr in self._constants:
                raise ConstantError(attr)
            else:
                self.__dict__[attr] = val

    def __dir__(self):
        return super().__dir__() + list(self._constants.keys())

When subclassing this, constants you create will be protected:

class Example(AllowConstants):
    def __init__(self, a, b):
        super().__init__()
        self.constant("b", b)
        self.a = a

    def try_a(self, value):
        self.a = value

    def try_b(self, value):
        self.b = value

    def __str__(self):
        return str({"a": self.a, "b": self.b})

    def __repr__(self):
        return self.__str__()


example = Example(1, 2)
print(example)  # {'a': 1, 'b': 2}

example.try_a(5)
print(example)  # {'a': 5, 'b': 2}

example.try_b(6)  # ConstantError: b

example.a = 7
print(example)  # {'a': 7, 'b': 2}

example.b = 8  # ConstantError: b

print(hasattr(example, "b"))  # True

#  To show that constants really do immediately become constant: 

class AnotherExample(AllowConstants):
    def __init__(self):
        super().__init__()
        self.constant("a", 2)
        print(self.a)
        self.a=3


AnotherExample()  # 2  ConstantError: a


# finally, for class constants:
class YetAnotherExample(Example):
    _class_constants = {
        'BLA': 3
    }

    def __init__(self, a, b):
        super().__init__(a,b)

    def try_BLA(self, value):
        self.BLA = value

ex3 = YetAnotherExample(10, 20)
ex3.BLA  # 3
ex3.try_BLA(10)  # ConstantError: BLA
ex3.BLA = 4  # ConstantError: BLA

Constants are local (every instance of classes inheriting from AllowConstants will have their own constants), act as normal attributes as long as they are not being re-assigned, and writing classes that inherit from this allows for more or less the same style as with languages that do support constants.

Also, if you want to prevent anyone from changing the values by directly accessing instance._constants, you can use one of the many containers not allowing this that were suggested in other answers. Finally, if you really feel you need to, you could prevent people from setting all of instance._constants to a new dictionary through some more attribute access of AllowConstants. (Of course none of this is very pythonic, but that's besides the point).

Edit (since making python unpythonic is a fun game): In order to make inheritance a bit easier you could modify AllowConstants as follows:

class AllowConstants:
    _constants = None
    _class_constants = None

    def __init__(self):
        self._constants = {}
        self._update_class_constants()

    def __init_subclass__(cls):
        """
        Without this, it is necessary to set _class_constants in any subclass of any class that has class constants
        """
        if cls._class_constants is not None:
            #prevent trouble where _class_constants is not overwritten
            possible_cases = cls.__mro__[1:-1] #0 will have cls and -1 will have object
            for case in possible_cases:
                if cls._class_constants is case._class_constants:
                    cls._class_constants = None
                    break

    def _update_class_constants(self):
        """
        Help with the inheritance of class constants
        """
        for superclass in self.__class__.__mro__:
            if hasattr(superclass, "_class_constants"):
                sccc = superclass._class_constants
                if sccc is not None:
                    for key in sccc:
                        if key in self._constants:
                            raise ConstantError(key)
                    self._constants.update(sccc)

    def constant(self, name, value):
        assert isinstance(name, str)
        assert self._constants is not None, "AllowConstants was not initialized"
        if name in self._constants or name in self.__dict__:
            raise ConstantError(name)
        self._constants[name] = value

    def __getattr__(self, attr):
        if attr in self._constants:
            return self._constants[attr]
        raise AttributeError(attr)

    def __setattr__(self, attr, val):
        if self._constants is None:
            # not finished initialization
            self.__dict__[attr] = val
        else:
            if attr in self._constants:
                raise ConstantError(attr)
            else:
                self.__dict__[attr] = val

    def __dir__(self):
        return super().__dir__() + list(self._constants.keys())

That way you can just do:

class Example(AllowConstants):
    _class_constants = {
        "BLA": 2
    }
    def __init__(self, a, b):
        super().__init__()
        self.constant("b", b)
        self.a = a

    def try_a(self, value):
        self.a = value

    def try_b(self, value):
        self.b = value

    def __str__(self):
        return str({"a": self.a, "b": self.b})

    def __repr__(self):
        return self.__str__()


class ChildExample1(Example):
    _class_constants = {
        "BLI": 88
    }


class ChildExample2(Example):
    _class_constants = {
        "BLA": 44
    }


example = ChildExample1(2,3)
print(example.BLA)  # 2
example.BLA = 8  # ConstantError BLA
print(example.BLI)  # 88
example.BLI = 8  # ConstantError BLI

example = ChildExample2(2,3)  # ConstantError BLA
Wolford answered 30/3, 2020 at 17:3 Comment(0)
W
1

You can use Tuple for constant variable :

• A tuple is a collection which is ordered and unchangeable

my_tuple = (1, "Hello", 3.4)
print(my_tuple[0])
Windage answered 21/7, 2020 at 6:23 Comment(0)
O
0

well.. even though this is outdated, let me add my 2 cents here :-)

class ConstDict(dict):
    def __init__(self, *args, **kwargs):
        super(ConstDict, self).__init__(*args, **kwargs)

    def __setitem__(self, key, value):
        if key in self:
            raise ValueError("Value %s already exists" % (key))
        super(ConstDict, self).__setitem__(key, value)

Instead of ValueError to break, you can prevent any update happening there. One advantage of this is that you can add constants dynamically in the program but you cannot change once a constant is set. Also you can add any rule or whatsoever before setting a constant(something like key must be a string or a lower case string or upper case string and so on before setting the key)

However, I do not see any importance of setting constants in Python. No optimizations can happen like in C and hence it is something that is not required, I guess.

Ouabain answered 20/1, 2015 at 13:3 Comment(0)
D
0

Simply you can just:

STRING_CONSTANT = "hi"
NUMBER_CONSTANT = 89

hope that makes everything much simpler

Distil answered 6/5, 2018 at 9:15 Comment(0)
L
0

Can't help but provide my own very light minimalist metaclass implementation (which may appear as a variation from the previous metaclass answer).

Constants are stored inside a container class (no instantiation needed). Values can be set once, but cannot be changed (or deleted) once they are set.

Personally I currently have no use-case for this, but was a fun exercise.

class MetaConstant(type):
    ''' Metaclass that allows underlying class to store constants at class-level (subclass instance not needed).
        Non-existent attributes of underlying class (constants) can be set initially, but cannot be changed or deleted.
    '''

    def __setattr__(klass, attr, value):
        'If attribute (constant) doesn\'t exist, set value. If attribute exists, raise AttributeError.'
        if hasattr(klass, attr):
            raise AttributeError(f'Can\'t change the value of the constant {klass.__name__}.{attr} to {value}'
                                 f' (the value of {klass.__name__}.{attr} is already set to'
                                 f' {getattr(klass, attr)}).')
        super().__setattr__(attr, value)

    def __delattr__(klass, attr):
        if hasattr(klass, attr):
            raise AttributeError(f'Can\'t delete constant {klass.__name__}.{attr}'
                                 f' (set to {getattr(klass, attr)}).')


class Constants(metaclass=MetaConstant):
    'Container class for constants. No instantiation required.'
    #pass               # uncomment if no constants set upon class creation
    B = 'Six'           # sets Constants.B to 'Six'


Constants.B = 6         # AttributeError
del Constants.B         # AttributeError

Constants.A = 'Five'    # sets Constants.A to 'Five'
Constants.A = 5         # AttributeError
del Constants.A         # AttributeError

Feel free to suggest improvements.

Lapful answered 28/1, 2020 at 20:44 Comment(0)
T
0

Note: This is a terrible idea and a terrible implementation. Also it only works for the small example at the end, a full implementation would mean lots of work, which I'm too lazy to do. Also audit hooks are probably not available before Python 3.8.

I mostly-answered another question and it turns out it's related to this one. The idea is that you can take advantage of audit hooks to catch the execution of every line, parse the code object, and if it fills some condition (e.g. a certain prefix and has been defined once) you can throw an error.

You'd probably have to support other assignment types (e.g. for imported stuff, maybe for locals inside functions, with unpacking, etc), not use globals since that dict can easily be modified, actually investigate if this is secure, accept the performance penalty this implementation will have for your whole application, make sure this works outside the REPL, inside ipython, etc etc. Anyway, here's we go:

>>> import sys
>>> import ast
>>> import dis
>>> import types
>>> 
>>> 
>>> def hook(name, tup):
...     if name == "exec" and tup:
...         if tup and isinstance(tup[0], types.CodeType):
...             code = tup[0]
...             store_instruction_arg = None
...             instructions = [dis.opname[op] for op in code.co_code]
...             
...             for i, instruction in enumerate(instructions):
...                 if instruction == "STORE_NAME":
...                     store_instruction_arg = code.co_code[i + 1]
...                     break
...             
...             if store_instruction_arg is not None:
...                 var_name = code.co_names[store_instruction_arg]
...                 if var_name in globals():
...                     raise Exception("Cannot re-assign variable")
... 
>>> 
>>> sys.addaudithook(hook)
>>> 
>>> a = '123'
>>> a = 456
Traceback (most recent call last):
  File "<stdin>", line 16, in hook
Exception: Cannot re-assign variable
>>> 
>>> a
'123'

If you end up going this way, which you shouldn't, other than fixes and generalization to the code you'll probably want to find a way to only make some stuff constant, e.g. only those objects with a special prefix or only objects that have some annotation.

Tobey answered 11/10, 2021 at 15:49 Comment(0)
V
-2

Extending Raufio's answer, add a __repr__ to return the value.

class const(object):
    def __init__(self, val):
        super(const, self).__setattr__("value", val)
    def __setattr__(self, name, val):
        raise ValueError("Trying to change a constant value", self)
    def __repr__(self):
        return ('{0}'.format(self.value))

dt = const(float(0.01))
print dt

then the object behaves a little more like you might expect, you can access it directly rather then '.value'

Vallee answered 26/9, 2013 at 15:10 Comment(1)
No. The result is not a constant. dt = 5 is accepted without complaint. In Raufio's answer, while one can also overwrite it, the result will cause a complaint on the next usage dt.value. So is a less dangerous failure. You have nullified the benefit of his solution.Fixed
K
-2

You can emulate constant variables with help of the next class. An example of usage:

# Const
const = Const().add(two=2, three=3)

print 'const.two: ', const.two
print 'const.three: ', const.three

const.add(four=4)

print 'const.four: ', const.four

#const.four = 5 # a error here: four is a constant

const.add(six=6)

print 'const.six: ', const.six

const2 = Const().add(five=5) # creating a new namespace with Const()
print 'const2.five: ', const2.five
#print 'const2.four: ', const2.four # a error here: four does not exist in const2 namespace

const2.add(five=26)

Call the constructor when you want to start a new constant namespace. Note that the class is under protection from unexpected modifying sequence type constants when Martelli's const class is not.

The source is below.

from copy import copy

class Const(object):
"A class to create objects with constant fields."

def __init__(self):
    object.__setattr__(self, '_names', [])


def add(self, **nameVals):
    for name, val in nameVals.iteritems():          
        if hasattr(self, name):
            raise ConstError('A field with a name \'%s\' is already exist in Const class.' % name)

        setattr(self, name, copy(val)) # set up getter

        self._names.append(name)

    return self


def __setattr__(self, name, val):
    if name in self._names:
        raise ConstError('You cannot change a value of a stored constant.')

    object.__setattr__(self, name, val)
Kosel answered 27/3, 2017 at 16:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.