Есть ли способ объявить константу в Python? В Java мы можем создавать постоянные значения таким образом:
public static final String CONST_NAME = "Name";
Что такое эквивалент объявления Java-декларации выше в Python?
Есть ли способ объявить константу в Python? В Java мы можем создавать постоянные значения таким образом:
public static final String CONST_NAME = "Name";
Что такое эквивалент объявления Java-декларации выше в Python?
Нет, нет. Вы не можете объявить переменную или значение как константу в Python. Просто не меняйте его.
Если вы находитесь в классе, эквивалент будет:
class Foo(object):
CONST_NAME = "Name"
Если нет, это просто
CONST_NAME = "Name"
Но вы можете захотеть взглянуть на фрагмент кода Константы в Python от Alex Martelli.
Нет ключевого слова const
, как на других языках, однако возможно создать свойство, в котором имеет функцию "getter" для чтения данных, но no "setter function" , чтобы перезаписать данные. Это существенно защищает идентификатор от изменения.
Вот альтернативная реализация, использующая свойство класса:
Обратите внимание, что код далеко не прост для читателя, интересующегося константами. См. Объяснение ниже
def constant(f):
def fset(self, value):
raise TypeError
def fget(self):
return f()
return property(fget, fset)
class _Const(object):
@constant
def FOO():
return 0xBAADFACE
@constant
def BAR():
return 0xDEADBEEF
CONST = _Const()
print CONST.FOO
##3131964110
CONST.FOO = 0
##Traceback (most recent call last):
## ...
## CONST.FOO = 0
##TypeError: None
Объяснение кода:
constant
, которая принимает выражение и использует его для построения "getter" - функции, которая возвращает значение выражения только.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 кажется устаревшим.
property
для создания объекта, который может быть "установлен" или "получить".property
имеет два первых параметра: fset
и fget
.property
В 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
Недавно я нашел очень краткое обновление, которое автоматически __dict__
значимые сообщения об ошибках и предотвращает доступ через __dict__
:
class CONST(object):
__slots__ = ()
FOO = 1234
CONST = CONST()
# ----------
print(CONST.FOO) # 1234
CONST.FOO = 4321 # AttributeError: 'CONST' object attribute 'FOO' is read-only
CONST.__dict__['FOO'] = 4321 # AttributeError: 'CONST' object has no attribute '__dict__'
CONST.BAR = 5678 # AttributeError: 'CONST' object has no attribute 'BAR'
Мы определяем себя, как сделать себя экземпляром, а затем используем слоты, чтобы гарантировать, что никакие дополнительные атрибуты не могут быть добавлены. Это также удаляет __dict__
доступа __dict__
. Конечно, весь объект все еще может быть переопределен.
Edit - оригинальное решение
Я, вероятно, здесь не хватает трюк, но это, кажется, работает для меня:
class CONST(object):
FOO = 1234
def __setattr__(self, *_):
pass
CONST = CONST()
#----------
print CONST.FOO # 1234
CONST.FOO = 4321
CONST.BAR = 5678
print CONST.FOO # Still 1234!
print CONST.BAR # Oops AttributeError
Создание экземпляра позволяет волшебному методу __setattr__
и перехватывать попытки установить переменную FOO
. Вы можете выбросить здесь исключение, если хотите. Создание экземпляра по имени класса предотвращает доступ напрямую через класс.
Это общая боль для одного значения, но вы можете присоединить много к вашему объекту CONST
. Имя высшего класса также кажется немного нелепым, но я думаю, что в целом оно довольно лаконично.
Python не имеет констант.
Возможно, самая простая альтернатива - определить для нее функцию:
def MY_CONSTANT():
return 42
MY_CONSTANT()
теперь имеет все функциональные возможности константы (плюс несколько раздражающих скобок).
В дополнение к двум верхним ответам (просто используйте переменные с именами 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, я был бы благодарен за отзывы.: -)
Изменение: Добавлен пример кода для 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.
Свойства - один из способов создания констант. Вы можете сделать это, объявив свойство getter, но игнорируя setter. Например:
class MyFinalProperty(object):
@property
def name(self):
return "John"
Вы можете взглянуть на статью, которую я написал, чтобы найти больше способов использования свойств Python.
Вот реализация класса "Константы", который создает экземпляры с постоянными атрибутами только для чтения. Например, можно использовать Nums.PI
для получения значения, которое было инициализировано как 3.14159
, а Nums.PI = 22
вызывает исключение.
# ---------- Constants.py ----------
class Constants(object):
"""
Create objects with read-only (constant) attributes.
Example:
Nums = Constants(ONE=1, PI=3.14159, DefaultWidth=100.0)
print 10 + Nums.PI
print '----- Following line is deliberate ValueError -----'
Nums.PI = 22
"""
def __init__(self, *args, **kwargs):
self._d = dict(*args, **kwargs)
def __iter__(self):
return iter(self._d)
def __len__(self):
return len(self._d)
# NOTE: This is only called if self lacks the attribute.
# So it does not interfere with get of 'self._d', etc.
def __getattr__(self, name):
return self._d[name]
# ASSUMES '_..' attribute is OK to set. Need this to initialize 'self._d', etc.
#If use as keys, they won't be constant.
def __setattr__(self, name, value):
if (name[0] == '_'):
super(Constants, self).__setattr__(name, value)
else:
raise ValueError("setattr while locked", self)
if (__name__ == "__main__"):
# Usage example.
Nums = Constants(ONE=1, PI=3.14159, DefaultWidth=100.0)
print 10 + Nums.PI
print '----- Following line is deliberate ValueError -----'
Nums.PI = 22
Спасибо @MikeGraham FrozenDict, который я использовал в качестве отправной точки. Изменено, поэтому вместо Nums['ONE']
используется синтаксис Nums.ONE
.
И спасибо ответу @Raufio, за идею переопределить __setattr __.
Или для реализации с большей функциональностью, смотрите @Hans_meine's named_constants на GitHub
Вы можете использовать namedtuple в качестве обходного пути для эффективного создания константы, которая работает так же, как статическая конечная переменная в Java (Java-"константа"). Поскольку обходные пути идут, это отчасти изящно. (Более элегантный подход - просто улучшить язык Python - какой язык позволяет вам переопределить math.pi
? - но я отвлекся.)
(Когда я пишу это, я понимаю другой ответ на этот вопрос, упомянутый namedtuple, но я продолжу здесь, потому что я покажу синтаксис, который более точно соответствует тому, что вы ожидаете в Java, так как нет необходимости создавать именованный введите как namedtuple заставляет вас делать.)
Следуя вашему примеру, вы помните, что в Java мы должны определить константу внутри некоторого класса; потому что вы не упомянули имя класса, давайте назовем его Foo
. Вот класс Java:
public class Foo {
public static final String CONST_NAME = "Name";
}
Здесь эквивалент Python.
from collections import namedtuple
Foo = namedtuple('_Foo', 'CONST_NAME')('Name')
Ключевой момент, который я хочу добавить, заключается в том, что вам не нужен отдельный тип Foo
("анонимный именованный кортеж" был бы хорош, даже если это звучит как оксюморон), поэтому мы называем наш namedtuple _Foo
так что, надеюсь, это не ускользнет от импорта модулей.
Вторым моментом здесь является то, что мы немедленно создаем экземпляр именного кортежа, называя его Foo
; нет необходимости делать это в отдельном шаге (если вы этого не хотите). Теперь вы можете делать то же, что и в Java:
>>> Foo.CONST_NAME
'Name'
Но вы не можете назначить это:
>>> Foo.CONST_NAME = 'bar'
…
AttributeError: can't set attribute
Подтверждение: я думал, что изобрел подход именованных кортежей, но потом я вижу, что кто-то другой дал аналогичный (хотя и менее компактный) ответ. Затем я также заметил, что называются кортежами " в Python?, что указывает на то, что sys.version_info
теперь именованный кортеж, поэтому, возможно, стандартная библиотека Python уже предложила эту идею гораздо раньше.
Обратите внимание, что, к сожалению (это все еще Python), вы можете полностью стереть все назначение Foo
:
>>> Foo = 'bar'
(Facepalm)
Но, по крайней мере, мы предотвращаем изменение значения Foo.CONST_NAME
, и это лучше, чем ничего. Удачи.
Я бы сделал класс, который переопределяет метод __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
отображал и знал, что вы выполняете операции с константами (может быть, это не самый "пуфонический" способ).
Кортеж технически квалифицируется как константа, так как кортеж будет вызывать ошибку, если вы попытаетесь изменить одно из своих значений. Если вы хотите объявить кортеж с одним значением, поместите запятую после ее единственного значения, например:
my_tuple = (0 """Or any other value""",)
Чтобы проверить это значение переменной, используйте что-то похожее на это:
if my_tuple[0] == 0:
#Code goes here
Если вы попытаетесь изменить это значение, будет поднята ошибка.
К сожалению, у Питона еще нет констант, и это позор. 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'
Заметки:
Протестировано с Python3.4 и Python3.5 (я использую 'tox' для него)
Среда тестирования:
,
$ uname -a
Linux wlysenko-Aspire 3.13.0-37-generic #64-Ubuntu SMP Mon Sep 22 21:28:38 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
Питоновский способ объявления "констант" в основном представляет собой переменную уровня модуля:
RED = 1
GREEN = 2
BLUE = 3
И затем напишите свои классы или функции. Поскольку константы почти всегда целые числа, и они также неизменны в Python, у вас очень мало шансов изменить его.
Если, конечно, если вы явно установили RED = 2
.
Словари Python изменяемы, поэтому они не кажутся хорошим способом объявления констант:
>>> constants = {"foo":1, "bar":2}
>>> print constants
{'foo': 1, 'bar': 2}
>>> constants["bar"] = 3
>>> print constants
{'foo': 1, 'bar': 3}
Мы можем создать объект дескриптора.
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
В python константа - это просто переменная с именем во всех столицах, со словами, разделенными символом подчеркивания,
например
DAYS_IN_WEEK = 7
Значение изменчиво, так как вы можете его изменить. Но, учитывая правила для названия, скажите, что вы постоянны, почему бы вам? Я имею в виду, это ваша программа в конце концов!
Это подход, используемый во всем python. По той же причине нет private
ключевого слова. Префикс имени с подчеркиванием, и вы знаете, что он является закрытым. Код может нарушить правило... так же, как программист может удалить ключевое слово private в любом случае.
Python мог бы добавить ключевое слово const
... но программист мог удалить ключевое слово, а затем изменить константу, если захочет, но зачем это делать? Если вы хотите нарушить правило, вы можете изменить правило в любом случае. Но зачем беспокоиться о том, чтобы нарушить правило, если имя делает намерение понятным?
Может быть, есть какой-то единичный тест, где имеет смысл применить изменение к ценности? Чтобы узнать, что происходит в течение 8-дневной недели, даже если в реальном мире количество дней в неделю не может быть изменено. Если язык остановился, вы делаете исключение, если есть только один случай, вам нужно нарушить правило... вам тогда придется прекратить объявлять его как константу, даже если он по-прежнему является константой в приложении, и существует просто этот тестовый пример, который видит, что произойдет, если он будет изменен.
Все имя верхнего регистра говорит о том, что оно предназначено для постоянной. Вот что важно. Не язык, навязывающий ограничения на код, который у вас есть, может измениться.
Это философия питона.
Существует более чистый способ сделать это с помощью namedtuple:
from collections import namedtuple
def make_consts(name, **kwargs):
return namedtuple(name, kwargs.keys())(**kwargs)
Пример использования
CONSTS = make_consts("baz1",
foo=1,
bar=2)
При таком подходе вы можете пронумеровать ваши константы.
Может быть, библиотека 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.
Вот трюк, если вы хотите константы и не заботитесь о своих значениях:
Просто определите пустые классы.
например:
class RED:
pass
class BLUE:
pass
Вы можете использовать StringVar или IntVar и т.д., ваша константа const_val
val = 'Stackoverflow'
const_val = StringVar(val)
const.trace('w', reverse)
def reverse(*args):
const_val.set(val)
Вы можете сделать это с помощью 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
В Python константы не существуют. Но вы можете указать, что переменная является константой и не должна изменяться, добавив CONST_
или CONSTANT_
в начало имени переменной или CONST_
имя переменной в BLOCK CAPITALS и указав, что она является константой в комментарии:
myVariable = 0
CONST_daysInWeek = 7 # This is a constant - do not change its value.
CONSTANT_daysInMonth = 30 # This is also a constant - do not change this value.
Просто вы можете просто:
STRING_CONSTANT = "hi"
NUMBER_CONSTANT = 89
надеюсь, что все намного проще
Нет идеального способа сделать это. Насколько я понимаю, большинство программистов просто воспользуются идентификатором, поэтому PI = 3.142 можно легко понять как константу.
С другой стороны, если вы хотите что-то, что на самом деле действует как постоянное, я не уверен, что вы его найдете. Что бы вы ни делали, всегда будет какой-то способ редактирования "константы", поэтому он не будет действительно постоянным. Вот очень простой, грязный пример:
def define(name, value):
if (name + str(id(name))) not in globals():
globals()[name + str(id(name))] = value
def constant(name):
return globals()[name + str(id(name))]
define("PI",3.142)
print(constant("PI"))
Похоже, что это сделает константу в стиле PHP.
В действительности все, что требуется, чтобы кто-то изменил значение, таков:
globals()["PI"+str(id("PI"))] = 3.1415
Это то же самое для всех других решений, которые вы найдете здесь - даже умных, которые делают класс и переопределяют метод атрибутов set - всегда будет вокруг них. Это как раз то, как Python.
Моя рекомендация состоит в том, чтобы просто избежать всех хлопот и просто использовать ваши идентификаторы. На самом деле это была бы не настоящая константа, но опять же ничего не было бы.
(Этот параграф должен был быть комментарием к тем ответам здесь и там, в которых упоминается namedtuple
, но слишком namedtuple
, чтобы вписаться в комментарий, так что вот оно.)
Вышеупомянутый подход, названный выше, определенно новаторский. Однако, ради полноты, в конце раздела NamedTuple официальной документации он гласит:
перечисленные константы могут быть реализованы с помощью названных кортежей, но проще и эффективнее использовать простую декларацию класса:
class Status: open, pending, closed = range(3)
Другими словами, форматы официальной документации предпочитают использовать практический способ, а не фактически выполнять поведение, доступное только для чтения. Я думаю, это станет еще одним примером Zen of Python:
Простой лучше, чем сложный.
практичность превосходит чистоту.
В моем случае мне понадобились непреложные bytearrays для реализации крипто-библиотеки, содержащей много буквенных чисел, которые я хотел обеспечить, были постоянными.
Этот ответ работает, но попытка переназначения элементов bytearray не вызывает ошибки.
def const(func):
'''implement const decorator'''
def fset(self, val):
'''attempting to set a const raises `ConstError`'''
class ConstError(TypeError):
'''special exception for const reassignment'''
pass
raise ConstError
def fget(self):
'''get a const'''
return func()
return property(fget, fset)
class Consts(object):
'''contain all constants'''
@const
def C1():
'''reassignment to C1 fails silently'''
return bytearray.fromhex('deadbeef')
@const
def pi():
'''is immutable'''
return 3.141592653589793
Константы неизменяемы, но постоянное задание присваивания не выполняется:
>>> c = Consts()
>>> c.pi = 6.283185307179586 # (https://en.wikipedia.org/wiki/Tau_(2%CF%80))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "consts.py", line 9, in fset
raise ConstError
__main__.ConstError
>>> c.C1[0] = 0
>>> c.C1[0]
222
>>> c.C1
bytearray(b'\xde\xad\xbe\xef')
Более мощный, простой и, возможно, даже более "питонический" подход предполагает использование объектов памяти (объектов буфера в <= python-2.6).
import sys
PY_VER = sys.version.split()[0].split('.')
if int(PY_VER[0]) == 2:
if int(PY_VER[1]) < 6:
raise NotImplementedError
elif int(PY_VER[1]) == 6:
memoryview = buffer
class ConstArray(object):
'''represent a constant bytearray'''
def __init__(self, init):
'''
create a hidden bytearray and expose a memoryview of that bytearray for
read-only use
'''
if int(PY_VER[1]) == 6:
self.__array = bytearray(init.decode('hex'))
else:
self.__array = bytearray.fromhex(init)
self.array = memoryview(self.__array)
def __str__(self):
return str(self.__array)
def __getitem__(self, *args, **kwargs):
return self.array.__getitem__(*args, **kwargs)
Назначение элемента ConstArray - это TypeError
:
>>> C1 = ConstArray('deadbeef')
>>> C1[0] = 0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'ConstArray' object does not support item assignment
>>> C1[0]
222
Я пишу утилиту lib для python const: kkconst - pypi поддержка str, int, float, datetime
экземпляр константного поля будет поддерживать поведение своего базового типа.
Например:
from __future__ import print_function
from kkconst import (
BaseConst,
ConstFloatField,
)
class MathConst(BaseConst):
PI = ConstFloatField(3.1415926, verbose_name=u"Pi")
E = ConstFloatField(2.7182818284, verbose_name=u"mathematical constant") # Euler number"
GOLDEN_RATIO = ConstFloatField(0.6180339887, verbose_name=u"Golden Ratio")
magic_num = MathConst.GOLDEN_RATIO
assert isinstance(magic_num, ConstFloatField)
assert isinstance(magic_num, float)
print(magic_num) # 0.6180339887
print(magic_num.verbose_name) # Golden Ratio
более подробное использование вы можете прочитать pypi url: pypi или github
Вы можете обернуть константу в массив numpy, пометить ее только для записи и всегда вызывать ее по нулевому индексу.
import numpy as np
# declare a constant
CONSTANT = 'hello'
# put constant in numpy and make read only
CONSTANT = np.array([CONSTANT])
CONSTANT.flags.writeable = False
# alternatively: CONSTANT.setflags(write=0)
# call our constant using 0 index
print 'CONSTANT %s' % CONSTANT[0]
# attempt to modify our constant with try/except
new_value = 'goodbye'
try:
CONSTANT[0] = new_value
except:
print "cannot change CONSTANT to '%s' it value '%s' is immutable" % (
new_value, CONSTANT[0])
# attempt to modify our constant producing ValueError
CONSTANT[0] = new_value
>>>
CONSTANT hello
cannot change CONSTANT to 'goodbye' it value 'hello' is immutable
Traceback (most recent call last):
File "shuffle_test.py", line 15, in <module>
CONSTANT[0] = new_value
ValueError: assignment destination is read-only
конечно, это защищает только содержимое numpy, а не переменную "CONSTANT"; вы все равно можете:
CONSTANT = 'foo'
и CONSTANT
изменились бы, однако это быстро сгенерировало бы TypeError при первом вызове CONSTANT[0]
в script.
хотя... Я полагаю, если вы в какой-то момент изменили его на
CONSTANT = [1,2,3]
теперь вы больше не получите TypeError. хммм....
https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.setflags.html
PEP 591 имеет квалификатор 'final'. Принудительное выполнение - до проверки типов.
Так что вы можете сделать:
MY_CONSTANT: Final = 12407