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

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

public static final String CONST_NAME = "Name";

Что такое эквивалент объявления Java-декларации выше в Python?

Ответы

Ответ 1

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

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

class Foo(object):
    CONST_NAME = "Name"

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

CONST_NAME = "Name"

Но вы можете захотеть взглянуть на фрагмент кода Константы в Python от Alex Martelli.

Ответ 2

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

Вот альтернативная реализация, использующая свойство класса:

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

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

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

  • Определите функцию constant, которая принимает выражение и использует его для построения "getter" - функции, которая возвращает значение выражения только.
  • Функция setter вызывает TypeError, поэтому он доступен только для чтения
  • Используйте функцию 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 кажется устаревшим.

  • Чтобы определить идентификатор FOO, firs определяют две функции (fset, fget - имена по моему выбору).
  • Затем используйте встроенную функцию property для создания объекта, который может быть "установлен" или "получить".
  • Обратите внимание, что функция property имеет два первых параметра: fset и fget.
  • Используйте тот факт, что мы выбрали эти имена для нашего собственного getter и setter и создаем словарный словарь с использованием ** (двойной звездочки), применяемый ко всем локальным определениям этой области для передачи параметров функции property

Ответ 3

В Python вместо того, чтобы язык что-то принуждает, люди используют соглашения об именах, например __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

Ответ 4

Недавно я нашел очень краткое обновление, которое автоматически __dict__ значимые сообщения об ошибках и предотвращает доступ через __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'

Мы определяем себя, как сделать себя экземпляром, а затем используем слоты, чтобы гарантировать, что никакие дополнительные атрибуты не могут быть добавлены. Это также удаляет __dict__ доступа __dict__. Конечно, весь объект все еще может быть переопределен.

Edit - оригинальное решение

Я, вероятно, здесь не хватает трюк, но это, кажется, работает для меня:

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

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

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

Ответ 5

Python не имеет констант.

Возможно, самая простая альтернатива - определить для нее функцию:

def MY_CONSTANT():
    return 42

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

Ответ 6

В дополнение к двум верхним ответам (просто используйте переменные с именами UPPERCASE или используйте свойства, чтобы значения были доступны только для чтения), я хочу упомянуть, что можно использовать метаклассы для реализации именованных констант. Я предоставляю очень простое решение, используя метаклассы в 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, я был бы благодарен за отзывы.: -)

Ответ 7

Изменение: Добавлен пример кода для 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.

Ответ 8

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

class MyFinalProperty(object):

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

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

Ответ 9

Вот реализация класса "Константы", который создает экземпляры с постоянными атрибутами только для чтения. Например, можно использовать 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's named_constants на GitHub

Ответ 10

Вы можете использовать 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 ("анонимный именованный кортеж" был бы хорош, даже если это звучит как оксюморон), поэтому мы называем наш namedtuple _Foo так что, надеюсь, это не ускользнет от импорта модулей.

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

>>> Foo.CONST_NAME
'Name'

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

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

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

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

>>> Foo = 'bar'

(Facepalm)

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

Ответ 11

Я бы сделал класс, который переопределяет метод __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 отображал и знал, что вы выполняете операции с константами (может быть, это не самый "пуфонический" способ).

Ответ 12

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

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

Чтобы проверить это значение переменной, используйте что-то похожее на это:

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

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

Ответ 13

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

файл 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. Не поддержка пользовательских типов и типа 'dict'

Заметки:

  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

Ответ 14

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

RED = 1
GREEN = 2
BLUE = 3

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

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

Ответ 15

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

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

Ответ 16

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

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) Если мы хотим работать с константами на уровне экземпляра, то:

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) если бы мы хотели создавать константы только на уровне класса, мы могли бы использовать метакласс, который служит контейнером для наших констант (наших объектов-дескрипторов); все нисходящие классы будут наследовать наши константы (наши объекты дескриптора) без какого-либо риска, который может быть изменен.

# 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

Если я создаю подкласс Foo, этот класс будет наследовать константу без возможности их изменения

class Bar(Foo): pass

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

Ответ 17

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

например

DAYS_IN_WEEK = 7

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

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

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

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

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

Это философия питона.

Ответ 18

Существует более чистый способ сделать это с помощью namedtuple:

from collections import namedtuple


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

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

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

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

Ответ 19

Может быть, библиотека pconst поможет вам (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.

Ответ 20

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

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

например:

class RED: 
    pass
class BLUE: 
    pass

Ответ 21

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

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

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

Ответ 22

Вы можете сделать это с помощью collections.namedtuple и 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

Ответ 23

В Python константы не существуют. Но вы можете указать, что переменная является константой и не должна изменяться, добавив CONST_ или CONSTANT_ в начало имени переменной или CONST_ имя переменной в BLOCK CAPITALS и указав, что она является константой в комментарии:

    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.

Ответ 24

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

STRING_CONSTANT = "hi"
NUMBER_CONSTANT = 89

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

Ответ 25

Нет идеального способа сделать это. Насколько я понимаю, большинство программистов просто воспользуются идентификатором, поэтому 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.

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

Ответ 26

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

Вышеупомянутый подход, названный выше, определенно новаторский. Однако, ради полноты, в конце раздела NamedTuple официальной документации он гласит:

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

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

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

Простой лучше, чем сложный.

практичность превосходит чистоту.

Ответ 27

В моем случае мне понадобились непреложные 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

Константы неизменяемы, но постоянное задание присваивания не выполняется:

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

Ответ 28

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

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

Например:

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

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

Ответ 29

Вы можете обернуть константу в массив 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 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 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"; вы все равно можете:

CONSTANT = 'foo'

и CONSTANT изменились бы, однако это быстро сгенерировало бы TypeError при первом вызове CONSTANT[0] в script.

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

CONSTANT = [1,2,3]

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

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

Ответ 30

PEP 591 имеет квалификатор 'final'. Принудительное выполнение - до проверки типов.

Так что вы можете сделать:

MY_CONSTANT: Final = 12407