на самом деле способ создания переменных только для чтения возможен через функцию / декоратор свойств 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. Только не меняй это.
Если вы находитесь в классе, эквивалент будет:
classFoo(object):
CONST_NAME ="Name"
если нет, это просто
CONST_NAME ="Name"
Но вы можете захотеть взглянуть на константы фрагмента кода в Python Алекса Мартелли.
Начиная с Python 3.8, существует typing.Finalпеременная аннотация, которая сообщает статическим контролерам типов (например, mypy), что ваша переменная не должна быть переназначена. Это самый близкий эквивалент Java final. Однако на самом деле это не предотвращает переназначение :
from typing importFinal
a:Final=1# Executes fine, but mypy will report an error if you run mypy on this:
a =2
Вместо того, чтобы делать то, что находится в «Константах в 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ключевого слова, как в других языках, однако можно создать свойство, которое имеет «функцию получения» для чтения данных, но нет «функции установки» для перезаписи данных. Это существенно защищает идентификатор от изменения.
Вот альтернативная реализация, использующая свойство класса:
Обратите внимание, что код не так прост для читателя, интересующегося константами. Смотрите объяснение ниже
Определить функцию constant которая принимает выражение и использует его для создания «получателя» - функции, которая возвращает только значение выражения.
Функция setter вызывает TypeError, поэтому она доступна только для чтения
Используйте constantфункцию, которую мы только что создали, как украшение для быстрого определения свойств только для чтения.
Обратите внимание, что декоратор @apply выглядит устаревшим.
Для определения идентификатора FOO, firs определяют две функции (fset, fget - имена на мой выбор).
Затем используйте встроенную propertyфункцию для создания объекта, который можно «установить» или «получить».
Обратите внимание, что propertyпервые два параметра функции названы fsetи fget.
Используйте тот факт, что мы выбрали эти самые имена для наших собственных методов получения и установки, и создаем словарь ключевых слов, используя ** (двойная звездочка), применяемый ко всем локальным определениям этой области, чтобы передать параметры в propertyфункцию
Основываясь на документации 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 =3Traceback(most recent call last):File"<stdin>", line 1,in<module>AttributeError: can't set attribute
Выполнение 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__маршрут доступа. Конечно, весь объект все еще может быть переопределен.
Редактировать - оригинальное решение
Я, вероятно, здесь не хватает трюк, но это, кажется, работает для меня:
Создание экземпляра позволяет магическому __setattr__методу включать и перехватывать попытки установить FOOпеременную. Вы можете выбросить здесь исключение, если хотите. Создание экземпляра по имени класса предотвращает доступ напрямую через класс.
Это общая боль для одного значения, но вы можете привязать много к вашему CONSTобъекту. Наличие высшего класса, имя класса также кажется немного нелепым, но я думаю, что в целом оно довольно лаконично.
Это лучший и самый четкий ответ, потому что он имеет наименьший «механизм», но большую функциональность. Возбуждение исключения важно, хотя ... не вариант.
Эрик Аронесты
Я разработал более короткий маршрут, который автоматически приводит к значительным ошибкам, но во многом в том же стиле. Я оставил оригинальную идею здесь для сравнения.
Джон Беттс
Как жаль, что вам все еще нужен этот CONST.префикс. Также в многомодульных ситуациях это будет сложно.
Alfe
1
Я думаю, что, как правило, вы в любом случае захотите сгруппировать константы в несколько связанных пакетов (а не иметь один гигантский объект CONST), так что это, вероятно, не так уж плохо.
Джон Беттс
Почему этот ответ еще так далеко? __slots__Решение является настолько элегантным и эффективным. Из всего, что я прочитал, это примерно так же близко к созданию констант в Python. Большое спасибо. И для всех интересующихся, вот блестящее и всестороннее объяснение __slots__магии.
JohnGalt
34
Python не имеет констант.
Возможно, самая простая альтернатива - определить для нее функцию:
def MY_CONSTANT():return42
MY_CONSTANT() теперь имеет всю функциональность константы (плюс несколько раздражающих скобок).
Я просто хотел добавить это предложение, но, к счастью, прокрутил список до ответов с низким рейтингом. Я надеюсь, что это будет далее поддержано, и я полностью согласен, что у этого есть все функциональные возможности константы, и это очень просто и прямо. Глядя на количество стандартного кода во всех сложных решениях, я нахожу брекеты относительно неприятными.
Yaccob
1
это самый простой ответ, хотя следует отметить, что он имеет некоторые накладные расходы и не остановит идиотов, изменяющих возвращаемое значение. Это просто предотвратит дальнейшее изменение кода в исходном коде
MrMesees
@MrMesees изменяет возвращаемое значение? Вы имеете в виду редактирование источника? Но от этого вы не защищены даже в C ++, где константы (вроде constexpr) являются реальными жесткими константами.
Руслан
@Ruslan имел в виду, что, поскольку у python нет constexpr, он не остановит редактируемое значение после его возвращения во внешний контекст. Ничто не было сделано для 42, чтобы обеспечить замороженное состояние в этом примере.
MrMesees
20
В дополнение к двум основным ответам (просто используйте переменные с именами UPPERCASE или используйте свойства, чтобы сделать значения доступными только для чтения), я хочу упомянуть, что можно использовать метаклассы для реализации именованных констант. Я предоставляю очень простое решение с использованием метаклассов в GitHub, которое может быть полезно, если вы хотите, чтобы значения были более информативными относительно их типа / имени:
>>>from named_constants importConstants>>>classColors(Constants):... black =0... red =1... white =15...>>> c =Colors.black
>>> c ==0True>>> c
Colors.black
>>> c.name()'black'>>>Colors(0)is c
True
Это немного более продвинутый Python, но все же очень простой в использовании и удобный. (Модуль имеет еще несколько функций, включая константы, доступные только для чтения, см. Его README.)
В разных репозиториях есть похожие решения, но, насколько мне известно, им либо не хватает одной из фундаментальных функций, которые я бы ожидал от констант (например, постоянных или произвольных типов), либо к ним добавлены эзотерические функции, которые сделать их менее общеприменимыми. Но YMMV, буду благодарен за отзыв. :-)
Мне нравится ваша реализация на 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, но игнорируя его. Например:
Недооцененное решение. Я только что реализовал это после того, как нашел эту страницу (а не этот ответ) и обернулся, чтобы добавить ее, если еще не сделал. Я хотел подчеркнуть полезность этого ответа.
Марк
18
Изменить: Добавлен пример кода для Python 3
Примечание: этот другой ответ выглядит так, как будто он обеспечивает гораздо более полную реализацию, похожую на следующую (с большим количеством функций).
Вы можете использовать namedtuple в качестве обходного пути для эффективного создания константы, которая работает так же, как статическая конечная переменная в Java (Java-«константа»). Как обходные пути идут, это вроде элегантно. (Более элегантный подход - просто улучшить язык Python - какой язык позволяет вам переопределитьmath.pi ? - но я отвлекся.)
(Когда я пишу это, я понимаю другой ответ на этот вопрос, упомянутый namedtuple, но я продолжу здесь, потому что я покажу синтаксис, который более точно соответствует тому, что вы ожидаете в Java, так как нет необходимости создавать именованный введите как namedtuple заставляет вас делать.)
Следуя вашему примеру, вы помните, что в Java мы должны определить константу внутри некоторого класса ; потому что вы не упомянули имя класса, давайте назовем его Foo. Вот класс Java:
public classFoo{
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стоимости, и это лучше, чем ничего. Удачи.
Вот реализация класса «Константы», который создает экземпляры с постоянными атрибутами только для чтения. Например, можно использовать Nums.PIдля получения значения, которое было инициализировано как 3.14159, и Nums.PI = 22вызывает исключение.
# ---------- Constants.py ----------classConstants(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:raiseValueError("setattr while locked", self)if(__name__ =="__main__"):# Usage example.Nums=Constants(ONE=1, PI=3.14159,DefaultWidth=100.0)print10+Nums.PI
print'----- Following line is deliberate ValueError -----'Nums.PI =22
Благодаря @MikeGraham's FrozenDict , который я использовал в качестве отправной точки. Изменено, поэтому вместо Nums['ONE']использования используется синтаксис Nums.ONE.
И спасибо ответу @ Raufio за идею переопределить __setattr __.
Python - это язык взрослых. Там нет защиты от чего-то вроде этого. Nums._d['PI'] = 22 Я полагаю, что сам язык не дает возможности помечать вещи как неизменяемые.
Ajay M
8
Кортеж технически квалифицируется как константа, так как кортеж вызовет ошибку, если вы попытаетесь изменить одно из его значений. Если вы хотите объявить кортеж с одним значением, поместите запятую после его единственного значения, например так:
my_tuple =(0"""Or any other value""",)
Чтобы проверить значение этой переменной, используйте что-то похожее на это:
if my_tuple[0]==0:#Code goes here
Если вы попытаетесь изменить это значение, возникнет ошибка.
Я хотел бы создать класс, который переопределяет __setattr__метод базового класса объектов, и обернуть мои константы этим, заметьте, что я использую Python 2.7:
class const(object):def __init__(self, val):
super(const, self).__setattr__("value", val)def __setattr__(self, name, val):raiseValueError("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показать и знать, что вы выполняете операции с константами (хотя, возможно, не самым «питоническим» способом).
+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',)classConstant(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__:raiseTypeError('Constanst can not be changed')ifnot isinstance(value, collections.Hashable):if isinstance(value, list):
value = tuple(value)elif isinstance(value, set):
value = frozenset(value)elif isinstance(value, dict):raiseTypeError('dict can not be used as constant')else:raiseValueError('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__:raiseTypeError('Constanst can not be deleted')raiseNameError("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 importConstantclassTestConstant(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 =.211def 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 =.211def 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 =.211def 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 =.211def 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 =Falsedef 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 =3212def 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 =Truedef 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 =Truedef 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 =Nonedef 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 =Nonedef 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 =1def 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'
Ноты:
Протестировано с Python3.4 и Python3.5 (я использую 'tox' для него)
Среда тестирования:
,
$ uname -a
Linux wlysenko-Aspire3.13.0-37-generic #64-Ubuntu SMP Mon Sep 22 21:28:38 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
Вы можете немного улучшить это, автоматически преобразовав словари в именованные кортежи
Питер Шорн
6
Pythonic способ объявления «констант» в основном является переменной уровня модуля:
RED =1
GREEN =2
BLUE =3
А затем напишите свои классы или функции. Поскольку константы почти всегда являются целыми числами, и они также неизменны в Python, у вас очень мало шансов изменить его.
Да, но блокирование возможности «явной установки RED = 2» - это полное преимущество (в других языках) возможности объявления имени переменной как «константы»!
ToolmakerSteve
6
Получите ли вы выгоду от блокировки этого? Наиболее полезная вещь в const - это, как правило, оптимизация компилятора, чего нет в Python. Хотите, чтобы что-то было постоянным? Просто не меняй это. Если вы беспокоитесь о том, чтобы кто-то еще изменил его, вы можете просто вывести его за рамки своей компетенции или просто осознать, что если кто-то его меняет, это его проблема, и им нужно заниматься этим, а не вам.
Кевин
@Kevin: «Получите ли вы выгоду ... », преимущество в том, staticчто у вас будет единое хранилище для значения для всех экземпляров класса? Если нет возможности действительно объявить статическую / классовую переменную.
минут
8
Основная проблема заключается в том, что некоторые могут рассматривать его как значение, являющееся источником истины, которое невозможно изменить, и использовать его в качестве источника истины в своем коде вместо введения магических значений (которые я часто вижу в Python) - и другие могут видеть это как то, что им разрешено менять по желанию. Когда кто-то изменяет глобальную переменную, и вы не можете сказать, где она изменилась, и приложение падает, потому что RED = «синий» вместо «красный», вы вводите совершенно ненужную проблему, которая уже была решена так просто и универсально понято.
Dagrooms
5
Мы можем создать объект дескриптора.
classConstant:def __init__(self,value=None):
self.value = value
def __get__(self,instance,owner):return self.value
def __set__(self,instance,value):raiseValueError("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 =100Traceback(most recent call last):File"<stdin>", line 1,in<module>ValueError:You can't change a constant
2) если бы мы хотели создавать константы только на уровне класса, мы могли бы использовать метакласс, который служит контейнером для наших констант (наших объектов-дескрипторов); все нисходящие классы будут наследовать наши константы (наши объекты дескриптора) без какого-либо риска, который может быть изменен.
# metaclass of my class FooclassFooMeta(type):pass# class FooclassFoo(metaclass=FooMeta):pass# I create constants in my metaclassFooMeta.NUM =Constant(0xff)FooMeta.NAME =Constant('FOO')>>>Foo.NUM #=> 255>>>Foo.NAME #=> 'FOO'>>>Foo.NUM =0#=> ValueError: You can't change a constant
Если я создаю подкласс Foo, этот класс будет наследовать константу без возможности их изменения
classBar(Foo):pass>>>Bar.NUM #=> 255>>>Bar.NUM =0#=> ValueError: You can't change a constant
В Python константа - это просто переменная с именем во всех столицах, слова разделяются символом подчеркивания,
например
DAYS_IN_WEEK = 7
Значение является изменяемым, так как вы можете изменить его. Но, учитывая правила для имени, говорят вам, что это константа, почему вы? Я имею в виду, это ваша программа в конце концов!
Это подход, принятый в Python. Здесь нетprivate ключевого слова по той же причине. Добавьте к имени подчеркивание, и вы знаете, что оно предназначено для частного использования. Код может нарушить правило .... так же, как программист может удалить ключевое слово private в любом случае.
Python мог бы добавить const ключевое слово ... но программист мог бы удалить ключевое слово и затем изменить константу, если он этого хочет, но зачем это делать? Если вы хотите нарушить правило, вы можете изменить правило в любом случае. Но зачем нарушать правило, если имя проясняет намерение?
Может быть, есть какой-то модульный тест, в котором имеет смысл применить изменение к значению? Чтобы увидеть, что происходит в течение 8-дневной недели, даже если в реальном мире количество дней в неделе нельзя изменить. Если язык перестал делать исключения, если есть только один случай, вам нужно нарушить правило ... тогда вам придется прекратить объявлять его как константу, даже если в приложении она все еще является константой, и есть только этот тестовый пример, который видит, что произойдет, если он будет изменен.
Имя в верхнем регистре говорит вам, что оно должно быть константой. Вот что важно. Не язык, накладывающий ограничения на код, вы в любом случае можете изменить.
Там нет идеального способа сделать это. Насколько я понимаю, большинство программистов просто используют заглавные буквы для идентификатора, поэтому PI = 3.142 может быть легко понята как константа.
С другой стороны, если вы хотите что-то, что действительно действует как константа, я не уверен, что вы найдете это. Что бы вы ни делали, всегда найдется способ отредактировать «константу», чтобы она не была константой. Вот очень простой грязный пример:
На самом деле все, что нужно для того, чтобы кто-то изменил значение, это:
globals()["PI"+str(id("PI"))]=3.1415
Это то же самое для всех других решений, которые вы найдете здесь - даже самых умных, которые создают класс и переопределяют метод set attribute - всегда найдется способ их обойти. Вот так выглядит Python.
Я рекомендую просто избегать всех хлопот и просто использовать ваши идентификаторы. Это не будет настоящей константой, но опять же ничего не будет.
Для всех, кто читает это, пожалуйста, имейте в виду, что, если вы установите изменяемый объект в качестве одной из этих констант, любой может изменить его внутреннее значение. например, давайте 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.
(Этот параграф должен был быть комментарием к тем ответам тут и там , где упоминалосьnamedtuple , но он становится слишком длинным, чтобы вписываться в комментарий, так что, вот так.)
Упомянутый выше подход namedtup определенно новаторский. Однако для полноты в конце раздела NamedTuple его официальной документации написано:
перечисляемые константы могут быть реализованы с именованными кортежами, но проще и эффективнее использовать простое объявление класса:
classStatus:
open, pending, closed = range(3)
Другими словами, официальная документация предпочитает использовать практический способ, а не фактически реализовывать поведение только для чтения. Я думаю, это станет еще одним примером Zen of Python :
Вот коллекция идиом, которые я создал как попытка улучшить некоторые из уже доступных ответов.
Я знаю, что использование константы не питон, и вы не должны делать это дома!
Тем не менее, Python такой динамичный язык! Этот форум показывает, как возможно создание конструкций, которые выглядят и ощущаются как константы. Этот ответ имеет своей основной целью изучить, что может быть выражено языком.
В этом посте я буду называть постоянную переменную постоянной ссылкой на значения (неизменяемые или иные). Более того, я говорю, что переменная имеет замороженное значение, когда она ссылается на изменяемый объект, который клиентский код не может обновить свои значения.
Пространство констант (SpaceConstants)
Эта идиома создает то, что выглядит как пространство имен постоянных переменных (или SpaceConstants). Это модификация фрагмента кода Алекса Мартелли, чтобы избежать использования объектов модуля. В частности, эта модификация использует то, что я называю фабрикой классов, потому что в функции SpaceConstants определен класс SpaceConstants и возвращен его экземпляр.
Я исследовал использование фабрики классов для реализации похожего на политику дизайна в Python в stackoverflow, а также в блоге .
Пространство замороженных значений (SpaceFrozenValues)
Эта следующая идиома представляет собой модификацию SpaceConstants, в которой ссылочные изменяемые объекты заморожены. Эта реализация использует то, что я называю общим закрытием между функциями setattr и getattr . Значение изменяемого объекта копируется и на него ссылается переменная cache, определяемая внутри функции общего закрытия. Он формирует то, что я называю закрытой защищенной копией изменяемого объекта .
Вы должны быть осторожны при использовании этой идиомы, потому что getattr возвращает значение кэша, делая глубокую копию. Эта операция может оказать значительное влияние на производительность на больших объектах!
from copy import deepcopy
defSpaceFrozenValues():
cache ={}def setattr(self, name, value):nonlocal cache
if name in cache:raiseAttributeError("Cannot reassign members")
cache[name]= deepcopy(value)def getattr(self, name):nonlocal cache
if name notin cache:raiseAttributeError("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 xprint(fv.x)# print "2"
fv.x =3# raise "AttributeError: Cannot reassign members"
fv.y ={'name':'y','value':2}# bind attribute yprint(fv.y)# print "{'name': 'y', 'value': 2}"
fv.y['name']='yprime'# you can try to change mutable objectsprint(fv.y)# print "{'name': 'y', 'value': 2}"
fv.y ={}# raise "AttributeError: Cannot reassign members"
Постоянное пространство (ConstantSpace)
Эта идиома является неизменным пространством имен постоянных переменных или ConstantSpace . Это сочетание удивительно простого ответа Джона Беттса в stackoverflow с фабрикой классов .
defConstantSpace(**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 changedprint(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
defFreezeProperty(value):
cache = deepcopy(value)return property(lambda self: deepcopy(cache))defFrozenSpace(**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 objectprint(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'"
В 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.
Кроме того, вы можете создать функцию, которая действует как константа:
В моем случае мне понадобились неизменяемые байтовые массивы для реализации криптографической библиотеки, содержащей много литеральных чисел, которые я хотел обеспечить постоянными.
Этот ответ работает, но попытка переназначения элементов bytearray не вызывает ошибку.
def const(func):'''implement const decorator'''def fset(self, val):'''attempting to set a const raises `ConstError`'''classConstError(TypeError):'''special exception for const reassignment'''passraiseConstErrordef fget(self):'''get a const'''return func()return property(fget, fset)classConsts(object):'''contain all constants'''@constdef C1():'''reassignment to C1 fails silently'''return bytearray.fromhex('deadbeef')@constdef pi():'''is immutable'''return3.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
raiseConstError
__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:raiseNotImplementedErrorelif int(PY_VER[1])==6:
memoryview = buffer
classConstArray(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]=0Traceback(most recent call last):File"<stdin>", line 1,in<module>TypeError:'ConstArray' object does not support item assignment
>>> C1[0]222
Я пишу утилиту lib для python const:
kkconst -
поддержка pypi str, int, float, datetime
Экземпляр поля const сохранит свое поведение базового типа.
Например:
from __future__ import print_function
from kkconst import(BaseConst,ConstFloatField,)classMathConst(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.6180339887print(magic_num.verbose_name)# Golden Ratio
Более подробно использование вы можете прочитать PyPi URL:
PyPi или GitHub
Вы можете заключить константу в массив numpy, отметить ее только для записи и всегда вызывать ее с нулевым индексом.
import numpy as np
# declare a constant
CONSTANT ='hello'# put constant in numpy and make read only
CONSTANT = np.array([CONSTANT])
CONSTANT.flags.writeable =False# alternatively: CONSTANT.setflags(write=0)# call our constant using 0 index print'CONSTANT %s'% CONSTANT[0]# attempt to modify our constant with try/except
new_value ='goodbye'try:
CONSTANT[0]= new_value
except:print"cannot change CONSTANT to '%s' it's value '%s' is immutable"%(
new_value, CONSTANT[0])# attempt to modify our constant producing ValueError
CONSTANT[0]= new_value
>>>
CONSTANT hello
cannot change CONSTANT to 'goodbye' it's value 'hello' is immutable
Traceback (most recent call last):
File "shuffle_test.py", line 15, in <module>
CONSTANT[0] = new_value
ValueError: assignment destination is read-only
конечно, это защищает только содержимое numpy, а не саму переменную "CONSTANT"; вы все еще можете сделать:
CONSTANT ='foo'
и CONSTANTизменится, однако это быстро вызовет ошибку TypeError при первом CONSTANT[0]вызове в скрипте.
хотя ... я полагаю, если вы когда-нибудь изменили это на
True=False
, а затем(2+2==4)==True
вернутьсяFalse
.SyntaxError: can't assign to keyword
. Это похоже на хорошую вещь.Ответы:
Нет там нет. Вы не можете объявить переменную или значение как константу в Python. Только не меняй это.
Если вы находитесь в классе, эквивалент будет:
если нет, это просто
Но вы можете захотеть взглянуть на константы фрагмента кода в Python Алекса Мартелли.
Начиная с Python 3.8, существует
typing.Final
переменная аннотация, которая сообщает статическим контролерам типов (например, mypy), что ваша переменная не должна быть переназначена. Это самый близкий эквивалент Javafinal
. Однако на самом деле это не предотвращает переназначение :источник
def MY_CONST_VALUE(): return 123
name.attribute
и могут содержать любое значение. Декларация простаNums = Constants(ONE=1, PI=3.14159, DefaultWidth=100.0)
, использование простоеprint 10 + Nums.PI
, попытка изменить результаты в исключенииNums.PI = 22
=> ValueError (..).Там нет
const
ключевого слова, как в других языках, однако можно создать свойство, которое имеет «функцию получения» для чтения данных, но нет «функции установки» для перезаписи данных. Это существенно защищает идентификатор от изменения.Вот альтернативная реализация, использующая свойство класса:
Обратите внимание, что код не так прост для читателя, интересующегося константами. Смотрите объяснение ниже
Объяснение кода:
constant
которая принимает выражение и использует его для создания «получателя» - функции, которая возвращает только значение выражения.constant
функцию, которую мы только что создали, как украшение для быстрого определения свойств только для чтения.И некоторым другим более старомодным способом:
(Код довольно хитрый, больше объяснений ниже)
Обратите внимание, что декоратор @apply выглядит устаревшим.
property
функцию для создания объекта, который можно «установить» или «получить».property
первые два параметра функции названыfset
иfget
.property
функциюисточник
AttributeError
иTypeError
, я думаю, что возникшее исключение должно быть новой ошибкой, которую я предлагаю назвать,ConstantError
или что-то подобное, которая является подклассомTypeError
. Раздел в документации, который заставляет меня думать, что: docs.python.org/2/library/exceptions.htmlCONST.__dict__['FOO'] = 7
В Python вместо того, чтобы что-то применять на языке, люди используют соглашения об именах, например,
__method
для частных методов и_method
для защищенных методов.Таким же образом вы можете просто объявить константу как все заглавные буквы, например
Если вы хотите, чтобы эта константа никогда не менялась, вы можете подключиться к доступу к атрибутам и выполнить трюки, но более простой подход - объявить функцию
Единственная проблема - везде, где вам придется делать MY_CONSTANT (), но опять-таки
MY_CONSTANT = "one"
правильный путь в python (обычно).Вы также можете использовать namedtuple для создания констант:
источник
def MY_CONSTANT(): return "one"
не остановит кого-либо, позже в коде, выполнениеMY_CONSTANT = "two"
(или повторное выделение функции).MY_CONSTANT = MY_CONSTANT()
, а в том, чтобы использоватьMY_CONSTANT()
в качестве константы. Конечно, это. Но это хорошо и очень соответствует принципу Python «Мы все здесь взрослые» - то есть разработчику редко будет запрещено принимать решение об отмене правила, когда у них есть веские причины и они знают, что делают.Недавно я нашел очень краткое обновление, которое автоматически выводит значимые сообщения об ошибках и предотвращает доступ через
__dict__
:Мы определяем себя, как сделать себя экземпляром, а затем используем слоты, чтобы гарантировать, что никакие дополнительные атрибуты не могут быть добавлены. Это также удаляет
__dict__
маршрут доступа. Конечно, весь объект все еще может быть переопределен.Редактировать - оригинальное решение
Я, вероятно, здесь не хватает трюк, но это, кажется, работает для меня:
Создание экземпляра позволяет магическому
__setattr__
методу включать и перехватывать попытки установитьFOO
переменную. Вы можете выбросить здесь исключение, если хотите. Создание экземпляра по имени класса предотвращает доступ напрямую через класс.Это общая боль для одного значения, но вы можете привязать много к вашему
CONST
объекту. Наличие высшего класса, имя класса также кажется немного нелепым, но я думаю, что в целом оно довольно лаконично.источник
CONST.
префикс. Также в многомодульных ситуациях это будет сложно.__slots__
Решение является настолько элегантным и эффективным. Из всего, что я прочитал, это примерно так же близко к созданию констант в Python. Большое спасибо. И для всех интересующихся, вот блестящее и всестороннее объяснение__slots__
магии.Python не имеет констант.
Возможно, самая простая альтернатива - определить для нее функцию:
MY_CONSTANT()
теперь имеет всю функциональность константы (плюс несколько раздражающих скобок).источник
constexpr
) являются реальными жесткими константами.В дополнение к двум основным ответам (просто используйте переменные с именами UPPERCASE или используйте свойства, чтобы сделать значения доступными только для чтения), я хочу упомянуть, что можно использовать метаклассы для реализации именованных констант. Я предоставляю очень простое решение с использованием метаклассов в GitHub, которое может быть полезно, если вы хотите, чтобы значения были более информативными относительно их типа / имени:
Это немного более продвинутый Python, но все же очень простой в использовании и удобный. (Модуль имеет еще несколько функций, включая константы, доступные только для чтения, см. Его README.)
В разных репозиториях есть похожие решения, но, насколько мне известно, им либо не хватает одной из фундаментальных функций, которые я бы ожидал от констант (например, постоянных или произвольных типов), либо к ним добавлены эзотерические функции, которые сделать их менее общеприменимыми. Но YMMV, буду благодарен за отзыв. :-)
источник
def enum(**enums): return type('Enum', (), enums)
.Numbers = enum(ONE=1, TWO=2, THREE='three')
, согласно stackoverflow.com/a/1695250/199364 , раздел «В более ранних версиях ...»Свойства являются одним из способов создания констант. Вы можете сделать это, объявив свойство getter, но игнорируя его. Например:
Вы можете взглянуть на статью, которую я написал, чтобы найти больше способов использовать свойства Python.
источник
Изменить: Добавлен пример кода для Python 3
Примечание: этот другой ответ выглядит так, как будто он обеспечивает гораздо более полную реализацию, похожую на следующую (с большим количеством функций).
Сначала создайте метакласс :
Это предотвращает изменение статических свойств. Затем создайте другой класс, который использует этот метакласс:
Или, если вы используете Python 3:
Это должно предотвратить изменение реквизита экземпляра. Чтобы использовать его, наследуйте:
Теперь реквизиты, доступ к которым осуществляется напрямую или через экземпляр, должны быть постоянными:
Вот пример выше в действии. Вот еще один пример для Python 3.
источник
Вы можете использовать namedtuple в качестве обходного пути для эффективного создания константы, которая работает так же, как статическая конечная переменная в Java (Java-«константа»). Как обходные пути идут, это вроде элегантно. (Более элегантный подход - просто улучшить язык Python - какой язык позволяет вам переопределить
math.pi
? - но я отвлекся.)(Когда я пишу это, я понимаю другой ответ на этот вопрос, упомянутый namedtuple, но я продолжу здесь, потому что я покажу синтаксис, который более точно соответствует тому, что вы ожидаете в Java, так как нет необходимости создавать именованный введите как namedtuple заставляет вас делать.)
Следуя вашему примеру, вы помните, что в Java мы должны определить константу внутри некоторого класса ; потому что вы не упомянули имя класса, давайте назовем его
Foo
. Вот класс Java:Вот эквивалентный Python.
Ключевой момент, который я хочу добавить, заключается в том, что вам не нужен отдельный
Foo
тип («анонимный именованный кортеж» был бы хорош, даже если это звучит как оксюморон), поэтому мы называем наш именованный кортеж_Foo
так, что, надеюсь, он не будет перейти к импорту модулей.Вторым моментом здесь является то, что мы немедленно создаем экземпляр nametuple, вызывая его
Foo
; нет необходимости делать это в отдельном шаге (если вы этого не хотите). Теперь вы можете делать то, что вы можете делать в Java:Но вы не можете назначить это:
Подтверждение: я думал, что изобрел подход именованных кортежей, но потом я вижу, что кто-то другой дал аналогичный (хотя и менее компактный) ответ. Тогда я также заметил, что такое «именованные кортежи» в Python? , который указывает на то, что
sys.version_info
теперь это именованный кортеж, поэтому, возможно, стандартная библиотека Python предложила эту идею гораздо раньше.Обратите внимание, что, к сожалению (это все еще Python), вы можете полностью стереть все
Foo
назначение:(Facepalm)
Но, по крайней мере, мы предотвращаем изменение
Foo.CONST_NAME
стоимости, и это лучше, чем ничего. Удачи.источник
PEP 591 имеет квалификатор 'final'. Исполнение до проверки типа.
Так что вы можете сделать:
Примечание:
Final
ключевое слово применимо только для версии Python 3.8источник
Вот реализация класса «Константы», который создает экземпляры с постоянными атрибутами только для чтения. Например, можно использовать
Nums.PI
для получения значения, которое было инициализировано как3.14159
, иNums.PI = 22
вызывает исключение.Благодаря @MikeGraham's FrozenDict , который я использовал в качестве отправной точки. Изменено, поэтому вместо
Nums['ONE']
использования используется синтаксисNums.ONE
.И спасибо ответу @ Raufio за идею переопределить __setattr __.
Или для реализации с большей функциональностью, смотрите @Hans_meine's named_constants на GitHub
источник
Nums._d['PI'] = 22
Я полагаю, что сам язык не дает возможности помечать вещи как неизменяемые.Кортеж технически квалифицируется как константа, так как кортеж вызовет ошибку, если вы попытаетесь изменить одно из его значений. Если вы хотите объявить кортеж с одним значением, поместите запятую после его единственного значения, например так:
Чтобы проверить значение этой переменной, используйте что-то похожее на это:
Если вы попытаетесь изменить это значение, возникнет ошибка.
источник
Я хотел бы создать класс, который переопределяет
__setattr__
метод базового класса объектов, и обернуть мои константы этим, заметьте, что я использую Python 2.7:Чтобы обернуть строку:
Это довольно просто, но если вы хотите использовать свои константы так же, как и непостоянный объект (без использования constObj.value), это будет немного интенсивнее. Возможно, это может вызвать проблемы, поэтому лучше
.value
показать и знать, что вы выполняете операции с константами (хотя, возможно, не самым «питоническим» способом).источник
def ONE(): return 1
легче использовать,ONE()
чем этот ответONE.value
.К сожалению, у Питона еще нет констант, и это позор. ES6 уже добавил константы поддержки в JavaScript ( https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/const ), поскольку это очень полезная вещь на любом языке программирования. Как и в других ответах в сообществе Python, в качестве констант используется соглашение - переменная верхнего регистра пользователя, но она не защищает от произвольных ошибок в коде. Если вам нравится, вы можете найти полезным решение для одного файла, как далее (см. Строки документации, как его использовать).
файл constants.py
Если этого недостаточно, см. Полный тестовый сценарий для этого.
Преимущества: 1. Доступ ко всем константам для всего проекта. 2. Строгий контроль значений констант.
Недостатки: 1. Не поддержка пользовательских типов и типа 'dict'
Ноты:
Протестировано с Python3.4 и Python3.5 (я использую 'tox' для него)
Среда тестирования:
,
источник
Pythonic способ объявления «констант» в основном является переменной уровня модуля:
А затем напишите свои классы или функции. Поскольку константы почти всегда являются целыми числами, и они также неизменны в Python, у вас очень мало шансов изменить его.
Если, конечно, если вы явно установите
RED = 2
.источник
RED = 2
» - это полное преимущество (в других языках) возможности объявления имени переменной как «константы»!static
что у вас будет единое хранилище для значения для всех экземпляров класса? Если нет возможности действительно объявить статическую / классовую переменную.Мы можем создать объект дескриптора.
1) Если мы хотим работать с константами на уровне экземпляра, то:
2) если бы мы хотели создавать константы только на уровне класса, мы могли бы использовать метакласс, который служит контейнером для наших констант (наших объектов-дескрипторов); все нисходящие классы будут наследовать наши константы (наши объекты дескриптора) без какого-либо риска, который может быть изменен.
Если я создаю подкласс Foo, этот класс будет наследовать константу без возможности их изменения
источник
Словари Python являются изменяемыми, поэтому они не кажутся хорошим способом объявления констант:
источник
Вот хитрость, если вам нужны константы и их значения не интересуют:
Просто определите пустые классы.
например:
источник
В Python константа - это просто переменная с именем во всех столицах, слова разделяются символом подчеркивания,
например
DAYS_IN_WEEK = 7
Значение является изменяемым, так как вы можете изменить его. Но, учитывая правила для имени, говорят вам, что это константа, почему вы? Я имею в виду, это ваша программа в конце концов!
Это подход, принятый в Python. Здесь нет
private
ключевого слова по той же причине. Добавьте к имени подчеркивание, и вы знаете, что оно предназначено для частного использования. Код может нарушить правило .... так же, как программист может удалить ключевое слово private в любом случае.Python мог бы добавить
const
ключевое слово ... но программист мог бы удалить ключевое слово и затем изменить константу, если он этого хочет, но зачем это делать? Если вы хотите нарушить правило, вы можете изменить правило в любом случае. Но зачем нарушать правило, если имя проясняет намерение?Может быть, есть какой-то модульный тест, в котором имеет смысл применить изменение к значению? Чтобы увидеть, что происходит в течение 8-дневной недели, даже если в реальном мире количество дней в неделе нельзя изменить. Если язык перестал делать исключения, если есть только один случай, вам нужно нарушить правило ... тогда вам придется прекратить объявлять его как константу, даже если в приложении она все еще является константой, и есть только этот тестовый пример, который видит, что произойдет, если он будет изменен.
Имя в верхнем регистре говорит вам, что оно должно быть константой. Вот что важно. Не язык, накладывающий ограничения на код, вы в любом случае можете изменить.
Это философия питона.
источник
Там нет идеального способа сделать это. Насколько я понимаю, большинство программистов просто используют заглавные буквы для идентификатора, поэтому PI = 3.142 может быть легко понята как константа.
С другой стороны, если вы хотите что-то, что действительно действует как константа, я не уверен, что вы найдете это. Что бы вы ни делали, всегда найдется способ отредактировать «константу», чтобы она не была константой. Вот очень простой грязный пример:
Похоже, это сделает константу в стиле PHP.
На самом деле все, что нужно для того, чтобы кто-то изменил значение, это:
Это то же самое для всех других решений, которые вы найдете здесь - даже самых умных, которые создают класс и переопределяют метод set attribute - всегда найдется способ их обойти. Вот так выглядит Python.
Я рекомендую просто избегать всех хлопот и просто использовать ваши идентификаторы. Это не будет настоящей константой, но опять же ничего не будет.
источник
Есть более чистый способ сделать это с namedtuple:
Пример использования
При таком подходе вы можете использовать пространство имен для ваших констант.
источник
Может быть, библиотека pconst поможет вам ( github ).
$ pip install pconst
[Out] Constant value of "APPLE_PRICE" is not editable.
источник
Вы можете использовать StringVar или IntVar и т. Д., Ваша константа - const_val
источник
Вы можете сделать это с помощью
collections.namedtuple
иitertools
:источник
(Этот параграф должен был быть комментарием к тем ответам тут и там , где упоминалось
namedtuple
, но он становится слишком длинным, чтобы вписываться в комментарий, так что, вот так.)Упомянутый выше подход namedtup определенно новаторский. Однако для полноты в конце раздела NamedTuple его официальной документации написано:
Другими словами, официальная документация предпочитает использовать практический способ, а не фактически реализовывать поведение только для чтения. Я думаю, это станет еще одним примером Zen of Python :
источник
Вот коллекция идиом, которые я создал как попытка улучшить некоторые из уже доступных ответов.
Я знаю, что использование константы не питон, и вы не должны делать это дома!
Тем не менее, Python такой динамичный язык! Этот форум показывает, как возможно создание конструкций, которые выглядят и ощущаются как константы. Этот ответ имеет своей основной целью изучить, что может быть выражено языком.
Пожалуйста, не будь слишком груб со мной :-).
Для более подробной информации я написал аккомпанирующий блог об этих идиомах .
В этом посте я буду называть постоянную переменную постоянной ссылкой на значения (неизменяемые или иные). Более того, я говорю, что переменная имеет замороженное значение, когда она ссылается на изменяемый объект, который клиентский код не может обновить свои значения.
Пространство констант (SpaceConstants)
Эта идиома создает то, что выглядит как пространство имен постоянных переменных (или SpaceConstants). Это модификация фрагмента кода Алекса Мартелли, чтобы избежать использования объектов модуля. В частности, эта модификация использует то, что я называю фабрикой классов, потому что в функции SpaceConstants определен класс SpaceConstants и возвращен его экземпляр.
Я исследовал использование фабрики классов для реализации похожего на политику дизайна в Python в stackoverflow, а также в блоге .
Пространство замороженных значений (SpaceFrozenValues)
Эта следующая идиома представляет собой модификацию SpaceConstants, в которой ссылочные изменяемые объекты заморожены. Эта реализация использует то, что я называю общим закрытием между функциями setattr и getattr . Значение изменяемого объекта копируется и на него ссылается переменная cache, определяемая внутри функции общего закрытия. Он формирует то, что я называю закрытой защищенной копией изменяемого объекта .
Вы должны быть осторожны при использовании этой идиомы, потому что getattr возвращает значение кэша, делая глубокую копию. Эта операция может оказать значительное влияние на производительность на больших объектах!
Постоянное пространство (ConstantSpace)
Эта идиома является неизменным пространством имен постоянных переменных или ConstantSpace . Это сочетание удивительно простого ответа Джона Беттса в stackoverflow с фабрикой классов .
Замороженное пространство (FrozenSpace)
Эта идиома является неизменным пространством имен замороженных переменных или FrozenSpace . Он получен из предыдущего шаблона, делая каждую переменную защищенным свойством путем закрытия сгенерированного класса FrozenSpace .
источник
В Python константы не существуют, но вы можете указать, что переменная является константой, и ее нельзя изменять, добавив
CONST_
в начало имя переменной и указав, что она является константой в комментарии:Кроме того, вы можете создать функцию, которая действует как константа:
источник
В моем случае мне понадобились неизменяемые байтовые массивы для реализации криптографической библиотеки, содержащей много литеральных чисел, которые я хотел обеспечить постоянными.
Этот ответ работает, но попытка переназначения элементов bytearray не вызывает ошибку.
Константы неизменны, но константное назначение байтового массива молча завершается неудачей:
Более мощный, простой и, возможно, еще более «питонический» подход предполагает использование объектов вида памяти (буферные объекты в <= python-2.6).
Назначение элемента ConstArray
TypeError
:источник
Я пишу утилиту lib для python const: kkconst - поддержка pypi str, int, float, datetime
Экземпляр поля const сохранит свое поведение базового типа.
Например:
Более подробно использование вы можете прочитать PyPi URL: PyPi или GitHub
источник
Вы можете заключить константу в массив numpy, отметить ее только для записи и всегда вызывать ее с нулевым индексом.
конечно, это защищает только содержимое numpy, а не саму переменную "CONSTANT"; вы все еще можете сделать:
и
CONSTANT
изменится, однако это быстро вызовет ошибку TypeError при первомCONSTANT[0]
вызове в скрипте.хотя ... я полагаю, если вы когда-нибудь изменили это на
Теперь вы больше не получите TypeError. хммм ....
https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.setflags.html
источник