How do I create a constant in Python?

后端 未结 30 2654
既然无缘
既然无缘 2020-11-22 09:07

Is there a way to declare a constant in Python? In Java we can create constant values in this manner:

public static          


        
相关标签:
30条回答
  • 2020-11-22 09:22

    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
    
    0 讨论(0)
  • 2020-11-22 09:22

    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
    
    0 讨论(0)
  • 2020-11-22 09:23

    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;
    
    0 讨论(0)
  • 2020-11-22 09:24

    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

    0 讨论(0)
  • 2020-11-22 09:25

    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
    
    0 讨论(0)
  • 2020-11-22 09:26

    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 CONST.FOO
    ##3131964110
    
    CONST.FOO = 0
    ##Traceback (most recent call last):
    ##    ...
    ##    CONST.FOO = 0
    ##TypeError: None
    

    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):
        @apply
        def FOO():
            def fset(self, value):
                raise TypeError
            def fget(self):
                return 0xBAADFACE
            return property(**locals())
    
    CONST = _Const()
    
    print CONST.FOO
    ##3131964110
    
    CONST.FOO = 0
    ##Traceback (most recent call last):
    ##    ...
    ##    CONST.FOO = 0
    ##TypeError: None
    

    Note that the @apply decorator seems to be deprecated.

    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
    0 讨论(0)
提交回复
热议问题