Reputation: 17075
How do I declare a constant in Python?
In Java, we do:
public static final String CONST_NAME = "Name";
Upvotes: 1435
Views: 1527624
Reputation: 42411
A better approach to creating constants in Python is to take inspiration from the various libraries over the years that take the hassle out of doing ordinary things. The short-con package does that for constants by providing a convenience wrapper around dataclasses.make_dataclass. [Disclaimer: I am the author of short-con.]
Values can be declared explicitly via either a dict
or kwargs
:
from short_con import constants, cons
Pieces = cons(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 = cons(NAMES) # All of these do the same thing.
Pieces = cons(xs)
Pieces = cons(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)
Upvotes: 3
Reputation: 1109
Python 3.13.0 suggestion:
Define an enum (StrEnum or IntEnum) such as the following, which happens to use string values:
from enum import StrEnum, auto
class DatabaseLocationEnum(StrEnum):
LOCAL: str = 'LOCAL'
CLOUD: str = 'CLOUD'
UNSPECIFIED = auto()
Any attempt to modify these pseudo-constants, such as the following:
DatabaseLocationEnum.UNSPECIFIED = 'CHANGED' # attempt to change a constant
results in the following error:
AttributeError: cannot reassign member 'UNSPECIFIED'
An IntEnum example I use in a simple Tic-Tac-Toe game:
from enum import IntEnum
class BoardLocation(IntEnum):
"""The symbolic locations of each possible position on the game board"""
TL = 0 # top-left
TM = 1 # top-middle
TR = 2 # top-right
ML = 3 # middle-left
M = 4 # middle
MR = 5 # middle-right
BL = 6 # bottom-left
BM = 7 # bottom-middle
BR = 8 # bottom-right
With the pseudo-constants for the board-locations, I use this:
board[BoardLocation.MR]
when looking at the board[] list rather than using "magic index numbers".
Upvotes: 2
Reputation: 816232
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
Upvotes: 1410
Reputation: 4869
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:
constant
that takes an expression, and uses it to construct a "getter" - a function that solely returns the value of the expression.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
property
function to construct an object that can be "set" or "get".property
function's first two parameters are named fset
and fget
.property
functionUpvotes: 446
Reputation: 913
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'
Upvotes: 16
Reputation: 692
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'
Upvotes: 3
Reputation: 88717
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
Upvotes: 164
Reputation: 1255
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.
Upvotes: 1
Reputation: 179
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).
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"
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"
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'"
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'"
Upvotes: 9
Reputation: 81
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")
Upvotes: 6
Reputation: 539
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])
Upvotes: 2
Reputation: 826
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
Upvotes: 2
Reputation: 1024
All of the answers given are essentially of two types:
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.
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.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. __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.
Upvotes: 3
Reputation: 2645
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.
Upvotes: 1
Reputation: 380
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.
Upvotes: 18
Reputation: 391
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
Upvotes: 20
Reputation:
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;
Upvotes: 3
Reputation: 137
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'
Upvotes: -1
Reputation: 1317
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).
Upvotes: 54
Reputation: 1111
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
Upvotes: 9
Reputation: 3328
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.
Upvotes: 110
Reputation: 51
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.
Upvotes: 5
Reputation: 528
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.
Upvotes: 7
Reputation: 19200
(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.
Upvotes: 5
Reputation: 41
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
Upvotes: 3
Reputation: 61
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.
Upvotes: 6
Reputation:
Simply you can just:
STRING_CONSTANT = "hi"
NUMBER_CONSTANT = 89
hope that makes everything much simpler
Upvotes: -1
Reputation: 2209
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.
Upvotes: 4
Reputation: 29
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)
Upvotes: 2
Reputation: 1011
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
Upvotes: 5