Как создать константу в Python?
Как задать константу в Python?
В Java это делается следующим образом:
public static final String CONST_NAME = "Name";
Как это можно реализовать в Python?
5 ответ(ов)
В Python нет ключевого слова const
, как в некоторых других языках. Однако можно создать свойство, которое имеет функцию "геттера" для чтения данных, но не имеет "сеттера" для их изменения. Это, по сути, защищает идентификатор от изменений.
Вот альтернативная реализация с использованием свойств класса:
Обратите внимание, что код далеко не прост для прочтения, если вы ищете константы. См. объяснение ниже.
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
Объяснение кода:
- Определите функцию
constant
, которая принимает выражение и использует его для создания "геттера" — функции, которая только возвращает значение выражения. - Функция сеттера вызывает
TypeError
, так что свойство становится только для чтения. - Используйте созданную функцию
constant
как декоратор для быстрого определения свойств только для чтения.
А вот более старомодный способ:
(Код довольно запутанный, дополнительное объяснение ниже)
class _Const(object):
def FOO():
def fset(self, value):
raise TypeError
def fget(self):
return 0xBAADFACE
return property(**locals())
FOO = FOO() # Определяем свойство.
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
- Чтобы определить идентификатор FOO, сначала создайте две функции (fset, fget — названия выбраны произвольно).
- Затем используйте встроенную функцию
property
для создания объекта, который можно "установить" или "получить". - Обратите внимание, что первые два параметра функции
property
называютсяfset
иfget
. - Воспользуйтесь тем, что мы выбрали эти названия для нашего геттера и сеттера, и создайте словарь с ключевыми словами, применив ** (двойное звездочка) ко всем локальным определениям этого области видимости, чтобы передать параметры функции
property
.
В ответ на ваш вопрос, вы можете использовать следующий подход для создания неизменяемого объекта с константами, который автоматически генерирует осмысленные сообщения об ошибках и предотвращает доступ через __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'
В этом фрагменте мы создаем класс CONST
, который реализует __slots__
, тем самым исключая возможность добавления дополнительных атрибутов. Это также удаляет возможность доступа к __dict__
. Однако, стоит отметить, что в целом объект все еще может быть переопределен.
Редактирование - Оригинальное решение
Возможно, я упускаю что-то важное, но, как кажется, это решение работает для меня:
class CONST(object):
FOO = 1234
def __setattr__(self, *_):
pass
CONST = CONST()
#----------
print CONST.FOO # 1234
CONST.FOO = 4321
CONST.BAR = 5678
print CONST.FOO # По-прежнему 1234!
print CONST.BAR # Ой, AttributeError
Создание экземпляра позволяет вызвать специальный метод __setattr__
, который перехватывает попытки изменить значение FOO
. Здесь можно выбросить исключение, если это необходимо. Инстанцирование экземпляра вместо прямого использования классового имени предотвращает доступ к атрибутам напрямую через класс.
Хотя это может показаться сложным для одной переменной, вы можете добавить множество значений в ваш объект CONST
. Несмотря на то, что использование верхнего класса и имя вроде CONST
может показаться не совсем идеальным, в целом этот подход довольно лаконичен.
Python действительно не имеет встроенной поддержки для констант. Однако одним из простейших вариантов является определение функции:
def MY_CONSTANT():
return 42
Теперь при вызове MY_CONSTANT()
вы получаете нечто, что работает как константа (плюс наличие скобок).
Хотя это и не совсем то, что подразумевается под константой, вы можете использовать заглавные буквы в названии функции, чтобы сигнализировать другим разработчикам о том, что значение не должно изменяться. Однако, важно помнить, что такая конструкция не предоставляет защиты от изменения значения, если это вам действительно нужно.
Другой подход - использовать модули, где вы определяете константы на уровне модуля:
MY_CONSTANT = 42
В этом случае, запись MY_CONSTANT
будет уже более привычной для восприятия как константы. Тем не менее, не существует механизма, который бы предотвратил изменение этой переменной, поэтому следите за тем, чтобы не изменять её значение в процессе работы программы.
Ваша проблема связана с попыткой переназначить значение элемента перечисления StringConsts
. Элементы Enum
являются неизменяемыми (immutable), что означает, что после их создания вы не можете изменять их значения.
Вот пример вашего кода с разъяснением:
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" # Ошибка: Cannot reassign
В этом примере StringConsts
наследует от str
и Enum
, что даёт вам возможность сравнивать элементы перечисления с обычными строками, при этом вы не теряете преимущества неизменяемости, предоставляемой перечислениями. Попытка переназначить StringConsts.ONE
приведет к ошибке, так как элементы перечисления не подлежат изменению.
Таким образом, данное решение может полностью заменить старый метод, представленный по ссылке http://code.activestate.com/recipes/65207-constants-in-python/?in=user-97991, так как сейчас вы можете использовать перечисления для хранения констант с хорошей поддержкой типов и безопасностью.
Вы можете объявить константные значения, используя замороженный класс данных, как показано ниже:
from dataclasses import dataclass
@dataclass(frozen=True)
class _Const:
SOME_STRING = 'some_string'
SOME_INT = 5
Const = _Const()
# В другом файле импортируйте Const и попробуйте
print(Const.SOME_STRING) # Всё в порядке!
Const.SOME_INT = 6 # dataclasses.FrozenInstanceError: невозможно присвоить значение полю 'SOME_INT'
Проблема, с которой вы столкнулись, заключается в том, что вы пытаетесь изменить значение поля в замороженном классе данных. Когда вы определяете класс как frozen
, поля этого класса становятся неизменяемыми (immutable), и попытка присвоить новое значение вызывает ошибку FrozenInstanceError
. Это работает идеально для констант, поскольку они создаются только один раз, когда экземпляр класса создается (в данном случае, при создании объекта Const
).
Если вам нужно изменить константы, вам понадобится переосмыслить способ их определения или использовать другой подход, например, обычный класс или просто модульные переменные.
Вызов функции модуля по его имени (строке)
Как напечатать без новой строки и пробела
Изменение типа столбца в pandas
Что такое __pycache__?
Как заполнить строку в Python пробелами?