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

992

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

public static final String CONST_NAME = "Name";

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

zfranciscus
источник
6
на самом деле способ создания переменных только для чтения возможен через функцию / декоратор свойств python . ответ на и является примером пользовательского использования этого. свойство является более общим, чем это, тем не менее, хороший анализ того, как это работает, находится на Атрибутах и ​​Методах Шалаба Чатурведи .
n611x007
20
ИМХО, принуждение к постоянству "не питон". В Python 2.7 вы даже можете написать True=False, а затем (2+2==4)==Trueвернуться False.
Оса
8
Как показывают другие ответы, нет никакого способа или необходимости объявлять константы. Но вы можете прочитать этот PEP о конвенциях. например, THIS_IS_A_CONSTANT
Расика Перера
34
@osa: Вы не можете сделать это в Python 3 - SyntaxError: can't assign to keyword. Это похоже на хорошую вещь.
naught101
3
Удивительно, но это не упоминалось до сих пор, но Enums может показаться хорошим способом определения перечисляемых констант.
cs95

Ответы:

973

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

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

class Foo(object):
    CONST_NAME = "Name"

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

CONST_NAME = "Name"

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


Начиная с Python 3.8, существует typing.Finalпеременная аннотация, которая сообщает статическим контролерам типов (например, mypy), что ваша переменная не должна быть переназначена. Это самый близкий эквивалент Java final. Однако на самом деле это не предотвращает переназначение :

from typing import Final

a: Final = 1

# Executes fine, but mypy will report an error if you run mypy on this:
a = 2
Феликс Клинг
источник
27
Вместо того, чтобы делать то, что находится в «Константах в Python», вы должны использовать функцию «свойство» или декоратор.
Сет Джонсон
26
Люди спрашивают о той же функции в Perl. Существует модуль импорта, который называется «use константой», но (AFAIK) это просто оболочка для создания крошечной функции, которая возвращает значение. Я делаю то же самое в Python. Пример:def MY_CONST_VALUE(): return 123
Кевинарпе
8
«Нет, нет». Правда, но, опираясь на работу других людей, я добавил ответ, гораздо ниже, с короткой и простой реализацией «Константы» для python 2.7 (в которой отсутствует «enum»). Они доступны только для чтения name.attributeи могут содержать любое значение. Декларация проста Nums = Constants(ONE=1, PI=3.14159, DefaultWidth=100.0), использование простое print 10 + Nums.PI, попытка изменить результаты в исключении Nums.PI = 22=> ValueError (..).
ToolmakerSteve
110
Только не меняй это. ты сделал мой день
Привет-Ангел
89
«Только не меняй это» вообще не помогает. Это не отвечает на вопрос, и я хотел бы предложить, чтобы это было удалено.
Бартек Банахевич
354

Там нет 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 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функцию для создания объекта, который можно «установить» или «получить».
  3. Обратите внимание, что propertyпервые два параметра функции названы fsetи fget.
  4. Используйте тот факт, что мы выбрали эти самые имена для наших собственных методов получения и установки, и создаем словарь ключевых слов, используя ** (двойная звездочка), применяемый ко всем локальным определениям этой области, чтобы передать параметры в propertyфункцию
фактура
источник
11
Основываясь на документации AttributeErrorи TypeError, я думаю, что возникшее исключение должно быть новой ошибкой, которую я предлагаю назвать, ConstantErrorили что-то подобное, которая является подклассом TypeError. Раздел в документации, который заставляет меня думать, что: docs.python.org/2/library/exceptions.html
ArtOfWarfare,
3
Я удивлен этим кодом. Почему в качестве аргумента шрифта методов FOO () и BAR () используется self? Моя IDE подчеркивает скобки на красном (ошибка «компиляции»). Я устал вставлять себя в это, но тогда я получаю ошибку.
user3770060
10
Если пойти на эти длины, то наметим явный недостаток языка Python. Почему они не почувствовали необходимости добавить это в Python 3. Я не могу поверить, что никто не предложил это, и я просто не вижу логики, стоящей за тем, как какой-то комитет действует «нет, константы»? Неа.'
Эндрю С
8
И ваше решение все еще может быть изменено определенным программистом Python с помощьюCONST.__dict__['FOO'] = 7
pppery
11
@ OscarSmith, я думаю, это улучшило бы дизайн «самодокументированного кода». Когда я ясно указываю в коде, что некоторые значения не могут быть изменены, это легче понять, чем читать весь исходный код и понимать, что некоторые значения никогда не меняются. Кроме того, он блокирует возможность кого-то изменить значение, которое должно быть постоянным. Помните: явное лучше, чем неявное.
Габриэль
112

В 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
Анураг Униял
источник
18
Выполнение def MY_CONSTANT(): return "one"не остановит кого-либо, позже в коде, выполнение MY_CONSTANT = "two"(или повторное выделение функции).
Мэтью Шинкель
6
@ MatthewSchinckel - это соглашение, также изменение MY_CONSTANT не изменит использования MY_CONSTANT (), но вызовет ошибку, и в python, если вы захотите что-то изменить, никакой хитрый трюк не сможет вас защитить.
Anurag Uniyal
3
Спасибо за использование подхода именованных кортежей. Определенно инновационный. Вы также можете найти мой «комментарий» здесь уместным.
RayLuo
@ MatthewSchinckel вы можете переопределить НИЧЕГО в Python, так что это не очень хороший момент.
cslotty
@MatthewSchinckel Идея не в том, чтобы писать MY_CONSTANT = MY_CONSTANT(), а в том, чтобы использовать MY_CONSTANT()в качестве константы. Конечно, это. Но это хорошо и очень соответствует принципу Python «Мы все здесь взрослые» - то есть разработчику редко будет запрещено принимать решение об отмене правила, когда у них есть веские причины и они знают, что делают.
jonathan.scholbach
70

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

Редактировать - оригинальное решение

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

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объекту. Наличие высшего класса, имя класса также кажется немного нелепым, но я думаю, что в целом оно довольно лаконично.

Джон Беттс
источник
11
Это лучший и самый четкий ответ, потому что он имеет наименьший «механизм», но большую функциональность. Возбуждение исключения важно, хотя ... не вариант.
Эрик Аронесты
Я разработал более короткий маршрут, который автоматически приводит к значительным ошибкам, но во многом в том же стиле. Я оставил оригинальную идею здесь для сравнения.
Джон Беттс
Как жаль, что вам все еще нужен этот CONST.префикс. Также в многомодульных ситуациях это будет сложно.
Alfe
1
Я думаю, что, как правило, вы в любом случае захотите сгруппировать константы в несколько связанных пакетов (а не иметь один гигантский объект CONST), так что это, вероятно, не так уж плохо.
Джон Беттс
Почему этот ответ еще так далеко? __slots__Решение является настолько элегантным и эффективным. Из всего, что я прочитал, это примерно так же близко к созданию констант в Python. Большое спасибо. И для всех интересующихся, вот блестящее и всестороннее объяснение __slots__магии.
JohnGalt
34

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

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

def MY_CONSTANT():
    return 42

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

Саид Бэйг
источник
1
Я просто хотел добавить это предложение, но, к счастью, прокрутил список до ответов с низким рейтингом. Я надеюсь, что это будет далее поддержано, и я полностью согласен, что у этого есть все функциональные возможности константы, и это очень просто и прямо. Глядя на количество стандартного кода во всех сложных решениях, я нахожу брекеты относительно неприятными.
Yaccob
1
это самый простой ответ, хотя следует отметить, что он имеет некоторые накладные расходы и не остановит идиотов, изменяющих возвращаемое значение. Это просто предотвратит дальнейшее изменение кода в исходном коде
MrMesees
@MrMesees изменяет возвращаемое значение? Вы имеете в виду редактирование источника? Но от этого вы не защищены даже в C ++, где константы (вроде constexpr) являются реальными жесткими константами.
Руслан
@Ruslan имел в виду, что, поскольку у python нет constexpr, он не остановит редактируемое значение после его возвращения во внешний контекст. Ничто не было сделано для 42, чтобы обеспечить замороженное состояние в этом примере.
MrMesees
20

В дополнение к двум основным ответам (просто используйте переменные с именами 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, буду благодарен за отзыв. :-)

hans_meine
источник
3
Мне нравится ваша реализация на GitHub. Я был почти готов написать базовый класс, который реализовал бы функцию обратного просмотра, но я вижу, что вы сделали это и многое другое!
Керр
Спасибо, @Kerr, это первый отзыв, который я получил и сделал меня счастливым. :-)
hans_meine
Потрясающие. Я только что попробовал это. Приятно иметь это как вариант. Хотя я еще не решил, достаточно ли я забочусь об аспекте только для чтения, чтобы использовать это, а не просто делать def enum(**enums): return type('Enum', (), enums). Numbers = enum(ONE=1, TWO=2, THREE='three'), согласно stackoverflow.com/a/1695250/199364 , раздел «В более ранних версиях ...»
ToolmakerSteve
19

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

class MyFinalProperty(object):

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

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

ПНВ
источник
Недооцененное решение. Я только что реализовал это после того, как нашел эту страницу (а не этот ответ) и обернулся, чтобы добавить ее, если еще не сделал. Я хотел подчеркнуть полезность этого ответа.
Марк
18

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

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

Вторым моментом здесь является то, что мы немедленно создаем экземпляр nametuple, вызывая его 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стоимости, и это лучше, чем ничего. Удачи.

Гаррет Уилсон
источник
Спасибо за использование подхода именованных кортежей. Определенно инновационный. Вы также можете найти мой «комментарий» здесь уместным.
RayLuo
10

PEP 591 имеет квалификатор 'final'. Исполнение до проверки типа.

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

MY_CONSTANT: Final = 12407

Примечание: Final ключевое слово применимо только для версии Python 3.8

Майк Эми
источник
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's FrozenDict , который я использовал в качестве отправной точки. Изменено, поэтому вместо Nums['ONE']использования используется синтаксис Nums.ONE.

И спасибо ответу @ Raufio за идею переопределить __setattr __.

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

ToolmakerSteve
источник
2
Python - это язык взрослых. Там нет защиты от чего-то вроде этого. Nums._d['PI'] = 22 Я полагаю, что сам язык не дает возможности помечать вещи как неизменяемые.
Ajay M
8

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

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

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

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

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

tobahhh
источник
7

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

Raufio
источник
+1 за интересный подход. Хотя не так чисто, как ответы, которые уже были предоставлены. И даже самое простое ранее предложенное решение def ONE(): return 1легче использовать, ONE()чем этот ответ ONE.value.
ToolmakerSteve
7

К сожалению, у Питона еще нет констант, и это позор. 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
PADYMKO
источник
Вы можете немного улучшить это, автоматически преобразовав словари в именованные кортежи
Питер Шорн
6

Pythonic способ объявления «констант» в основном является переменной уровня модуля:

RED = 1
GREEN = 2
BLUE = 3

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

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

Ксавье Хо
источник
21
Да, но блокирование возможности «явной установки RED = 2» - это полное преимущество (в других языках) возможности объявления имени переменной как «константы»!
ToolmakerSteve
6
Получите ли вы выгоду от блокировки этого? Наиболее полезная вещь в const - это, как правило, оптимизация компилятора, чего нет в Python. Хотите, чтобы что-то было постоянным? Просто не меняй это. Если вы беспокоитесь о том, чтобы кто-то еще изменил его, вы можете просто вывести его за рамки своей компетенции или просто осознать, что если кто-то его меняет, это его проблема, и им нужно заниматься этим, а не вам.
Кевин
@Kevin: «Получите ли вы выгоду ... », преимущество в том, staticчто у вас будет единое хранилище для значения для всех экземпляров класса? Если нет возможности действительно объявить статическую / классовую переменную.
минут
8
Основная проблема заключается в том, что некоторые могут рассматривать его как значение, являющееся источником истины, которое невозможно изменить, и использовать его в качестве источника истины в своем коде вместо введения магических значений (которые я часто вижу в Python) - и другие могут видеть это как то, что им разрешено менять по желанию. Когда кто-то изменяет глобальную переменную, и вы не можете сказать, где она изменилась, и приложение падает, потому что RED = «синий» вместо «красный», вы вводите совершенно ненужную проблему, которая уже была решена так просто и универсально понято.
Dagrooms
5

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

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
MVP
источник
4

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

>>> constants = {"foo":1, "bar":2}
>>> print constants
{'foo': 1, 'bar': 2}
>>> constants["bar"] = 3
>>> print constants
{'foo': 1, 'bar': 3}
n8boyd
источник
4

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

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

например:

class RED: 
    pass
class BLUE: 
    pass
Lym Zoy
источник
4

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

например

DAYS_IN_WEEK = 7

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

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

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

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

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

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

innov8
источник
4

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

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

Джон
источник
4

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

from collections import namedtuple


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

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

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

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

Хуан Игнасио Санчес
источник
Для всех, кто читает это, пожалуйста, имейте в виду, что, если вы установите изменяемый объект в качестве одной из этих констант, любой может изменить его внутреннее значение. например, давайте bar = [1, 2, 3], тогда вы можете сделать следующее: CONSTS.bar [1] = 'a', и оно не будет отклонено. Так что будьте осторожны с этим.
Хуан Игнасио Санчес
Вместо этого хакерского метода, который я сделал просто для забавы, я рекомендую использовать вместо этого декоратор свойств Python.
Хуан Игнасио Санчес
4

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

сим
источник
3

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

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

def reverse(*args):
    const_val.set(val)
Nqobizwe
источник
2

Вы можете сделать это с помощью 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
Jaunty Sankey
источник
2

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

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

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

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

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

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

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

RayLuo
источник
2

Вот коллекция идиом, которые я создал как попытка улучшить некоторые из уже доступных ответов.

Я знаю, что использование константы не питон, и вы не должны делать это дома!

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

Пожалуйста, не будь слишком груб со мной :-).

Для более подробной информации я написал аккомпанирующий блог об этих идиомах .

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

Пространство констант (SpaceConstants)

Эта идиома создает то, что выглядит как пространство имен постоянных переменных (или SpaceConstants). Это модификация фрагмента кода Алекса Мартелли, чтобы избежать использования объектов модуля. В частности, эта модификация использует то, что я называю фабрикой классов, потому что в функции SpaceConstants определен класс SpaceConstants и возвращен его экземпляр.

Я исследовал использование фабрики классов для реализации похожего на политику дизайна в Python в stackoverflow, а также в блоге .

def SpaceConstants():
    def setattr(self, name, value):
        if hasattr(self, name):
            raise AttributeError(
                "Cannot reassign members"
            )
        self.__dict__[name] = value
    cls = type('SpaceConstants', (), {
        '__setattr__': setattr
    })
    return cls()

sc = SpaceConstants()

print(sc.x) # raise "AttributeError: 'SpaceConstants' object has no attribute 'x'"
sc.x = 2 # bind attribute x
print(sc.x) # print "2"
sc.x = 3 # raise "AttributeError: Cannot reassign members"
sc.y = {'name': 'y', 'value': 2} # bind attribute y
print(sc.y) # print "{'name': 'y', 'value': 2}"
sc.y['name'] = 'yprime' # mutable object can be changed
print(sc.y) # print "{'name': 'yprime', 'value': 2}"
sc.y = {} # raise "AttributeError: Cannot reassign members"

Пространство замороженных значений (SpaceFrozenValues)

Эта следующая идиома представляет собой модификацию SpaceConstants, в которой ссылочные изменяемые объекты заморожены. Эта реализация использует то, что я называю общим закрытием между функциями setattr и getattr . Значение изменяемого объекта копируется и на него ссылается переменная cache, определяемая внутри функции общего закрытия. Он формирует то, что я называю закрытой защищенной копией изменяемого объекта .

Вы должны быть осторожны при использовании этой идиомы, потому что getattr возвращает значение кэша, делая глубокую копию. Эта операция может оказать значительное влияние на производительность на больших объектах!

from copy import deepcopy

def SpaceFrozenValues():
    cache = {}
    def setattr(self, name, value):
        nonlocal cache
        if name in cache:
            raise AttributeError(
                "Cannot reassign members"
            )
        cache[name] = deepcopy(value)
    def getattr(self, name):
        nonlocal cache
        if name not in cache:
            raise AttributeError(
                "Object has no attribute '{}'".format(name)
            )
        return deepcopy(cache[name])
    cls = type('SpaceFrozenValues', (),{
        '__getattr__': getattr,
        '__setattr__': setattr
    })
    return cls()

fv = SpaceFrozenValues()
print(fv.x) # AttributeError: Object has no attribute 'x'
fv.x = 2 # bind attribute x
print(fv.x) # print "2"
fv.x = 3 # raise "AttributeError: Cannot reassign members"
fv.y = {'name': 'y', 'value': 2} # bind attribute y
print(fv.y) # print "{'name': 'y', 'value': 2}"
fv.y['name'] = 'yprime' # you can try to change mutable objects
print(fv.y) # print "{'name': 'y', 'value': 2}"
fv.y = {} # raise "AttributeError: Cannot reassign members"

Постоянное пространство (ConstantSpace)

Эта идиома является неизменным пространством имен постоянных переменных или ConstantSpace . Это сочетание удивительно простого ответа Джона Беттса в stackoverflow с фабрикой классов .

def ConstantSpace(**args):
    args['__slots__'] = ()
    cls = type('ConstantSpace', (), args)
    return cls()

cs = ConstantSpace(
    x = 2,
    y = {'name': 'y', 'value': 2}
)

print(cs.x) # print "2"
cs.x = 3 # raise "AttributeError: 'ConstantSpace' object attribute 'x' is read-only"
print(cs.y) # print "{'name': 'y', 'value': 2}"
cs.y['name'] = 'yprime' # mutable object can be changed
print(cs.y) # print "{'name': 'yprime', 'value': 2}"
cs.y = {} # raise "AttributeError: 'ConstantSpace' object attribute 'x' is read-only"
cs.z = 3 # raise "AttributeError: 'ConstantSpace' object has no attribute 'z'"

Замороженное пространство (FrozenSpace)

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

from copy import deepcopy

def FreezeProperty(value):
    cache = deepcopy(value)
    return property(
        lambda self: deepcopy(cache)
    )

def FrozenSpace(**args):
    args = {k: FreezeProperty(v) for k, v in args.items()}
    args['__slots__'] = ()
    cls = type('FrozenSpace', (), args)
    return cls()

fs = FrozenSpace(
    x = 2,
    y = {'name': 'y', 'value': 2}
)

print(fs.x) # print "2"
fs.x = 3 # raise "AttributeError: 'FrozenSpace' object attribute 'x' is read-only"
print(fs.y) # print "{'name': 'y', 'value': 2}"
fs.y['name'] = 'yprime' # try to change mutable object
print(fs.y) # print "{'name': 'y', 'value': 2}"
fs.y = {} # raise "AttributeError: 'FrozenSpace' object attribute 'x' is read-only"
fs.z = 3 # raise "AttributeError: 'FrozenSpace' object has no attribute 'z'"
Виктор Базтерра
источник
2

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

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.

Кроме того, вы можете создать функцию, которая действует как константа:

def CONST_daysInWeek():
    return 7;

источник
1

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

Этот ответ работает, но попытка переназначения элементов 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
jxqz
источник
1

Я пишу утилиту 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

Более подробно использование вы можете прочитать PyPi URL: PyPi или GitHub

kaka_ace
источник
1

Вы можете заключить константу в массив 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"; вы все еще можете сделать:

CONSTANT = 'foo'

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

хотя ... я полагаю, если вы когда-нибудь изменили это на

CONSTANT = [1,2,3]

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

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

litepresence
источник