Как создать константу в Python?

есть ли способ объявить константу в Python? В Java мы можем создавать постоянные значения следующим образом:

public static final String CONST_NAME = "Name";

что эквивалентно приведенному выше объявлению константы Java в Python?

30 ответов


нет нет нет. Вы не можете объявить переменную или значение как константы в Python. Только не меняй его.

Если вы находитесь в классе, эквивалент будет:

class Foo(object):
    CONST_NAME = "Name"

если нет, то это просто

CONST_NAME = "Name"

но вы, возможно, захотите взглянуть на фрагмент кода константы в Python Алекс Мартелли.


нет const ключевое слово, как и в других языках, однако можно создать свойство, которое имеет функцию "getter" читать данные, но нет "сеттер"функция переписать данные. это существенно защищает идентификатор от изменения.

вот альтернативная реализация с использованием свойства класса:

обратите внимание, что код далек от простого для читателя, интересующегося константами. См. объяснение ниже

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

Объяснение Кода:

  1. определить функцию constant который принимает выражение и использует его для создания "геттера" - функции, которая возвращает только значение выражения.
  2. функция setter вызывает TypeError, поэтому она доступна только для чтения
  3. использовать constant функция, которую мы создали в качестве украшения, чтобы быстро определить только для чтения свойства.

и каким-то другим, более старомодным способом:

(код довольно сложный, больше объяснений ниже)

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

обратите внимание, что декоратор @apply кажется устаревшим.

  1. чтобы определить идентификатор FOO, firs определяют две функции (fset, fget - имена по моему выбору).
  2. тогда используйте встроенный property функция для построения объекта, который может быть " set "или"get".
  3. Примечание шляпа property первые два параметра функции называются fset и fget.
  4. используйте тот факт, что мы выбрали эти самые имена для нашего собственного геттера и сеттера и создали словарь ключевых слов, используя * * (двойную звездочку), примененный ко всем локальным определениям этой области, чтобы передать параметры в property функции

в Python вместо языка, применяющего что-то, люди используют соглашения об именах e.g __method для частных методов и с помощью _method для защищенных методов.

таким же образом вы можете просто объявить константу как все колпачки, например

MY_CONSTANT = "one"

если вы хотите, чтобы эта константа никогда не менялась, вы можете подключиться к доступу к атрибутам и делать трюки, но более простой подход-объявить функцию

def MY_CONSTANT():
    return "one"

только проблема везде, где вам придется делать MY_CONSTANT (), но опять MY_CONSTANT = "one" является правильным способом в python (обычно).

вы также можете использовать namedtuple для создания констант:

>>> 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

Я, наверное, упускаем, но это, кажется, работает для меня:

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

создание экземпляра позволяет magic __setattr__ метод, чтобы пнуть и перехватить попытки установить FOO переменной. Вы можете сделать исключение, если хотите. Создание экземпляра по имени класса предотвращает доступ непосредственно через класс.

это полная боль для одного значения, но вы можете прикрепить много к своему CONST "объект". Наличие высшего класса, имя класса также кажется немного гротти, но я думаю, что это довольно лаконично в целом.


Как вы, вероятно, уже знаете, Python не имеет констант : (

возможно, самой простой альтернативой является определение функции для него. Например.

def MY_CONSTANT():
    return 42

MY_CONSTANT() теперь имеет всю функциональность константы (плюс некоторые раздражающие фигурные скобки).


в дополнение к двум верхним ответам (просто используйте переменные с именами в верхнем регистре или используйте свойства, чтобы сделать значения доступными только для чтения), я хочу упомянуть, что можно использовать метаклассы для реализации имени константы. Я предоставляю очень простое решение, используя метаклассы в GitHub что может быть полезно, если вы хотите, чтобы значения были более информативными об их типе/имени:

>>> 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

Это немного более продвинутый Python, но все же очень простой в использовании и удобный. (Модуль имеет еще несколько функций, включая константы, доступные только для чтения, см. Его README.)

есть похожие решения, плавающие в различных репозиториях, но, насколько мне известно, они либо не имеют одной из фундаментальных особенностей, которые я ожидал бы от констант (например, быть постоянным или быть произвольного типа), либо у них есть эзотерические функции, которые делают их менее общеприменимыми. Но YMMV, я был бы благодарен за обратную связь. :-)


Edit: добавлен пример кода для Python 3

Примечание: этот и другие ответы похоже, что он обеспечивает гораздо более полную реализацию, подобную следующей (с большим количеством функций).

во-первых, сделать метакласс:

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

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

это предотвращает изменение статических свойств. Затем создайте другой класс, который использует этот метакласс:

class Const(object):
    __metaclass__ = MetaConst

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

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

или, если вы используете Python 3:

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

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

Это должно запретить изменение реквизита экземпляра. Чтобы использовать его, наследовать:

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

теперь реквизит, доступный напрямую или через экземпляр, должен быть постоянным:

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

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

здесь пример выше в действии. здесь еще один пример для Python 3.


вот реализация класса "константы", который создает экземпляры с атрибутами только для чтения (константы). Е. Г. можете использовать Nums.PI чтобы получить значение, которое было инициализировано как 3.14159 и Nums.PI = 22 вызывает исключение.

# ---------- 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

спасибо @MikeGraham 'FrozenDict с, который я использовал в качестве отправной точки. Изменилось, так что вместо Nums['ONE'] синтаксис использования Nums.ONE.

и благодаря ответу @Raufio, для идеи переопределить _ _ setattr __.

или для реализации с большей функциональностью см. @Hans_meine named_constants в GitHub


свойства-один из способов создания констант. Вы можете сделать это, объявив свойство getter, но игнорируя setter. Например:

class MyFinalProperty(object):

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

вы можете посмотреть на статья, которую я написал чтобы найти больше способов использования свойств Python.


Я бы сделал класс, который переопределяет __setattr__ метод базового класса объектов и оберните мои константы этим, обратите внимание, что я использую 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)

чтобы обернуть строку:

>>> 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'

это довольно просто, но если вы хотите использовать свои константы так же, как и непостоянный объект (без использования constObj.value), он будет немного более интенсивным. Возможно, это может вызвать проблемы, поэтому лучше всего сохранить .value показать и знать что вы делаете операции с константами (возможно, не самый "питонический" способ).


к сожалению, Python пока не имеет констант, и это позор. ES6 уже добавил константы поддержки в JavaScript (https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/const) так как это очень полезная вещь на любом языке программирования. Как ответили в других ответах в сообществе Python, используйте переменную верхнего регистра convention - user в качестве констант, но она не защищает от произвольных ошибок в коде. Если вы хотите, вы можете быть найдены полезным a однофайловое решение в качестве следующего (см. docstrings как его использовать).

файл 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()

Если этого недостаточно, см. Полный пример для этого.

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"})

преимущества: 1. Доступ ко всем константам для всего проекта 2. Строгий контроль значений констант

не хватает: 1. Не поддерживает пользовательские типы и тип 'словарь'

Примечания:

  1. протестировано с Python3.4 и Python3.5 (я использую "tox" для него)

  2. окружающая среда испытания:

.

$ 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

Питонический способ объявления "констант" в основном является переменной уровня модуля:

RED = 1
GREEN = 2
BLUE = 3

а затем напишите свои классы или функции. Поскольку константы почти всегда целые, и они также являются неизменными в Python, у вас очень мало шансов изменить его.

Если, конечно, вы явно не установили RED = 2.


кортеж технически квалифицируется как константа, как кортеж вызовет ошибку, если вы попытаетесь изменить одно из его значений. Если вы хотите объявить кортеж с одним значением, поместите запятую после его единственного значения, например:

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

чтобы проверить значение этой переменной, использовать что-то вроде этого:

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

Если вы попытаетесь изменить это значение, возникнет ошибка.


вы можете использовать namedtuple в качестве обходного пути для эффективного создания константы, которая работает так же, как статическая конечная переменная в Java ("константа"Java). Как обходные пути, это своего рода элегантный. (Более элегантным подходом было бы просто улучшить язык Python - - - какой язык позволяет переопределить math.pi? -- но я отвлекся.)

(когда я пишу это, я понимаю другой ответ на этот вопрос, упомянутый namedtuple, но я продолжу здесь, потому что я покажу синтаксис, который больше напоминает, что вы ожидаете в Java, так как нет необходимости создавать именованный тип как namedtuple заставляет вас делать.)

следуя вашему примеру, вы помните, что в Java мы должны определить константу внутри какого-то класса, потому что вы не упомянули имя класса, назовем его Foo. Вот класс Java:

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

вот эквивалентный Python.

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

ключевой момент, который я хочу добавить вот что вам не нужно отдельное Foo type ("анонимный Кортеж" было бы неплохо, хотя это звучит как оксюморон), поэтому мы называем наш namedtuple _Foo так что, надеюсь, он не убежит к импорту модулей.

второй момент здесь заключается в том, что мы немедленно создайте экземпляр из nametuple, называя его Foo; там нет необходимости делать это в отдельном шаге (если вы хотите). Теперь ты можешь делать то, что можешь. Java:

>>> Foo.CONST_NAME
'Name'

но вы не можете назначить это:

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

подтверждение: я думал, что изобрел подход namedtuple, но затем я вижу, что кто-то другой дал аналогичный (хотя и менее компактный) ответ. Тогда я тоже заметил что такое "именованные кортежи" в Python?, что указывает на то, что sys.version_info теперь является namedtuple, поэтому, возможно, стандартная библиотека Python уже придумала эту идею намного раньше.

обратите внимание, что к сожалению (это все еще Python), вы можете стереть все Foo задание в целом:

>>> Foo = 'bar'

(facepalm)

но по крайней мере мы предотвращая Foo.CONST_NAME значение от изменения, и это лучше, чем ничего. Удача.


словари Python изменчивы, поэтому они не кажутся хорошим способом объявить константы:

>>> constants = {"foo":1, "bar":2}
>>> print constants
{'foo': 1, 'bar': 2}
>>> constants["bar"] = 3
>>> print constants
{'foo': 1, 'bar': 3}

в python константа - это просто переменная с именем во всех заглавных буквах, со словами, разделенными символом подчеркивания,

Эл.г

DAYS_IN_WEEK = 7

значение изменчиво, так как вы можете его изменить. Но учитывая, что правила для имени говорят вам, что это константа, почему бы вам? В конце концов, это же твоя программа!

это подход, используемый во всем python. Нет private ключевое слово по той же причине. Префикс имени с подчеркивание, и вы знаете, что оно предназначено для частного использования. Код может нарушить правило....так же, как программист может удалить ключевое слово private в любом случае.

Python мог бы добавить const ключевое слово... но программист может удалить ключевое слово, а затем изменить константу, если захочет, но зачем это делать? Если вы хотите нарушить правило, вы можете изменить его в любом случае. Но зачем нарушать правило, если имя ясно выражает намерение?

может быть, есть какой-модульный тест где имеет смысл применить изменение к стоимости? Чтобы увидеть, что происходит в течение 8-дневной недели, хотя в реальном мире количество дней в неделе не может быть изменено. Если язык остановил вас сделать исключение, если есть только один случай, вам нужно нарушить правило...тогда вам придется прекратить объявлять его константой, даже если он все еще является константой в приложении, и есть только один тестовый случай, который видит, что произойдет, если он будет изменен.

все верхние название дела говорит вам, что оно предназначено быть константой. Вот что важно. Не язык, заставляющий ограничения на код, который вы можете изменить в любом случае.

это философия python.


просто вы можете просто:

STRING_CONSTANT = "hi"
NUMBER_CONSTANT = 89

надеюсь, что это делает все намного проще


в моем случае мне нужны были неизменяемые bytearrays для реализации криптографической библиотеки, содержащей много буквальных чисел, которые я хотел обеспечить постоянными.

ответ работает, но попытка переназначения элементов bytearray не вызывает ошибки.

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

константы неизменяемы, но постоянное назначение bytearray терпит неудачу молча:

>>> 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')

более мощный, простой, и, возможно, даже более подходящие для Python подход предполагает использование объектов memoryview (буферные объекты в

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-это 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

Я пишу util lib для Python const: kkconst - pypi поддержка str, int, float, datetime

экземпляр поля const сохранит поведение базового типа.

например:

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

подробнее использование вы можете прочитать url pypi: pypi или github


вы можете обернуть константу в массив numpy, отметить ее только для записи и всегда называть ее индексом ноль.

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

конечно, это защищает только содержимое numpy, а не саму переменную "константа"; вы все еще можете сделать:

CONSTANT = 'foo'

и CONSTANT изменится, однако это быстро бросит TypeError в первый раз CONSTANT[0] позже в сценарии.

хотя... Я полагаю, если вы в какой-то момент изменили его на

CONSTANT = [1,2,3]

теперь вы больше не получите TypeError. хммм....

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


мы можем создать объект дескриптора:

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")

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

вот трюк, если вы хотите константы и не заботитесь об их значениях:

просто определите пустые классы.

Эл.г:

class RED: 
    pass
class BLUE: 
    pass

вы можете использовать StringVar или IntVar и т. д. Ваша константа const_val

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

def reverse(*args):
    const_val.set(val)

в Python, константы не существует. Но вы можете указать, что переменная является постоянной и не должна изменяться путем добавления '_CONSTANT' в начале имени переменной, называя переменную ЗАГЛАВНЫМИ буквами и добавляя комментарий с помощью хэштега (' # ') например :

    normal_variable = 0
    CONSTANT_variable = 1    # This is a constant - do not change its value!   

расширяя ответ Рауфио, добавьте repr для возврата значения.

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

тогда объект ведет себя немного больше, как вы могли бы ожидать, вы можете получить к нему прямой доступ, а не".значение'


хорошо.. хотя это устарело, позвольте мне добавить мои 2 цента здесь: -)

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)

вместо ValueError для разрыва, вы можете предотвратить любое обновление происходит там. Одним из преимуществ этого является то, что вы можете динамически добавлять константы в программу, но не можете изменять их после установки константы. Также вы можете добавить любое правило или что-то еще перед установкой константы(что-то вроде ключа должно быть строкой или строкой нижнего регистра или строкой верхнего регистра и так далее перед установкой ключ)

однако я не вижу никакой важности установки констант в Python. Никакая оптимизация не может произойти, как в C, и, следовательно, это то, что не требуется, я думаю.


Вы можете эмулировать постоянные переменные с помощью следующего класса. Пример использования:

# 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)

вызовите конструктор, если вы хотите запустить новое пространство имен констант. Обратите внимание, что класс в защиту от неожиданного изменения констант типа последовательности, когда Мартелли const класса нет.

источник ниже.

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)

нет идеального способа сделать это. Насколько я понимаю, большинство программистов будут просто использовать идентификатор, поэтому PI = 3.142 можно легко понять как константу.

С другой стороны, если вы хотите что-то, что действительно действует как константа, я не уверен, что вы найдете его. Во всем, что вы делаете, всегда будет какой-то способ редактирования "константы", поэтому она не будет действительно константой. Вот очень простой, грязный пример:

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"))

похоже, что это будет сделайте константу в стиле PHP.

на самом деле все, что нужно для кого-то, чтобы изменить значение, это:

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

это то же самое для всех других решений, которые вы найдете здесь - даже умные, которые делают класс и переопределяют метод атрибута set - всегда будет способ обойти их. Вот каков Python.

моя рекомендация состоит в том, чтобы просто избежать всех хлопот и просто воспользоваться вашими идентификаторами. Это не было бы настоящей константой, но а потом опять ничего.


(этот абзац должен был быть комментарием к этим ответам здесь и здесь про namedtuple, но он становится слишком длинным, чтобы вписываться в комментарий, так что вот оно.)

подход namedtuple, упомянутый выше, определенно инновационный. Для полноты, однако, в конце раздела NamedTuple официальной документации, он читает:

перечислимые константы могут быть реализованы с именованными кортежами, но проще и эффективнее использовать простое объявление класса:

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

другими словами, официальная документация предпочитает использовать практический способ, а не фактически реализовывать поведение только для чтения. Думаю, это станет еще одним примером Дзен питона:

простое лучше, чем сложное.

практичность бьет чистоты.


есть более чистый способ сделать это с namedtuple:

from collections import namedtuple


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

Пример Использования

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

при таком подходе вы можете определить свои константы.