Как мне создать константу в Python? - PullRequest
846 голосов
/ 21 апреля 2010

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

public static final String CONST_NAME = "Name";

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

Ответы [ 33 ]

840 голосов
/ 21 апреля 2010

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

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

class Foo(object):
    CONST_NAME = "Name"

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

CONST_NAME = "Name"

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

323 голосов
/ 22 апреля 2010

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

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

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

def constant(f):
    def fset(self, value):
        raise TypeError
    def fget(self):
        return f()
    return property(fget, fset)

class _Const(object):
    @constant
    def FOO():
        return 0xBAADFACE
    @constant
    def BAR():
        return 0xDEADBEEF

CONST = _Const()

print CONST.FOO
##3131964110

CONST.FOO = 0
##Traceback (most recent call last):
##    ...
##    CONST.FOO = 0
##TypeError: None

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

  1. Определите функцию constant, которая принимает выражение и использует его для создания «получателя» - функции, которая исключительно возвращает значение выражения.
  2. Функция установки вызывает ошибку TypeError, поэтому она доступна только для чтения
  3. Используйте функцию constant, которую мы только что создали в качестве украшения, для быстрого определения свойств только для чтения.

И другим, более старомодным способом:

(код довольно сложный, дополнительные пояснения приведены ниже)

class _Const(object):
    @apply
    def FOO():
        def fset(self, value):
            raise TypeError
        def fget(self):
            return 0xBAADFACE
        return property(**locals())

CONST = _Const()

print CONST.FOO
##3131964110

CONST.FOO = 0
##Traceback (most recent call last):
##    ...
##    CONST.FOO = 0
##TypeError: None

Обратите внимание, что декоратор @apply устарел.

  1. Чтобы определить идентификатор FOO, сначала задайте две функции (fset, fget - имена на мой выбор).
  2. Затем используйте встроенную функцию property для создания объекта, который можно «установить» или «получить».
  3. Обратите внимание, что первые два параметра функции property называются fset и fget.
  4. Используйте тот факт, что мы выбрали эти самые имена для наших собственных методов получения и установки, и создаем словарь ключевых слов, используя ** (двойная звездочка), применяемый ко всем локальным определениям этой области, чтобы передать параметры в функцию property
99 голосов
/ 21 апреля 2010

В Python вместо принудительного применения языка люди используют соглашения об именах, например, __method для закрытых методов и _method для защищенных методов.

Таким же образом, вы можете просто объявить константу как все заглавные буквы, например

MY_CONSTANT = "one"

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

def MY_CONSTANT():
    return "one"

Единственная проблема везде, где вам придется выполнять MY_CONSTANT (), но опять же MY_CONSTANT = "one" - правильный путь в питоне (обычно).

Вы также можете использовать 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
47 голосов
/ 24 апреля 2014

Недавно я обнаружил очень краткое обновление, которое автоматически выводит значимые сообщения об ошибках и запрещает доступ через __dict__:

class CONST(object):
    __slots__ = ()
    FOO = 1234

CONST = CONST()

# ----------

print(CONST.FOO)    # 1234

CONST.FOO = 4321              # AttributeError: 'CONST' object attribute 'FOO' is read-only
CONST.__dict__['FOO'] = 4321  # AttributeError: 'CONST' object has no attribute '__dict__'
CONST.BAR = 5678              # AttributeError: 'CONST' object has no attribute 'BAR'

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

Редактировать - Исходное решение

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

class CONST(object):
    FOO = 1234

    def __setattr__(self, *_):
        pass

CONST = CONST()

#----------

print CONST.FOO    # 1234

CONST.FOO = 4321
CONST.BAR = 5678

print CONST.FOO    # Still 1234!
print CONST.BAR    # Oops AttributeError

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

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

26 голосов
/ 09 сентября 2017

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

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

def MY_CONSTANT():
    return 42

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

18 голосов
/ 11 октября 2013

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

16 голосов
/ 03 октября 2015

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

12 голосов
/ 13 июля 2017

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

class MyFinalProperty(object):

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

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

8 голосов
/ 11 декабря 2013

Вот реализация класса «Константы», который создает экземпляры с постоянными атрибутами только для чтения. Например. можно использовать Nums.PI для получения значения, которое было инициализировано как 3.14159, а Nums.PI = 22 вызывает исключение.

# ---------- Constants.py ----------
class Constants(object):
    """
    Create objects with read-only (constant) attributes.
    Example:
        Nums = Constants(ONE=1, PI=3.14159, DefaultWidth=100.0)
        print 10 + Nums.PI
        print '----- Following line is deliberate ValueError -----'
        Nums.PI = 22
    """

    def __init__(self, *args, **kwargs):
        self._d = dict(*args, **kwargs)

    def __iter__(self):
        return iter(self._d)

    def __len__(self):
        return len(self._d)

    # NOTE: This is only called if self lacks the attribute.
    # So it does not interfere with get of 'self._d', etc.
    def __getattr__(self, name):
        return self._d[name]

    # ASSUMES '_..' attribute is OK to set. Need this to initialize 'self._d', etc.
    #If use as keys, they won't be constant.
    def __setattr__(self, name, value):
        if (name[0] == '_'):
            super(Constants, self).__setattr__(name, value)
        else:
            raise ValueError("setattr while locked", self)

if (__name__ == "__main__"):
    # Usage example.
    Nums = Constants(ONE=1, PI=3.14159, DefaultWidth=100.0)
    print 10 + Nums.PI
    print '----- Following line is deliberate ValueError -----'
    Nums.PI = 22

Благодаря @ MikeGraham's FrozenDict , который я использовал в качестве отправной точки. Изменено, поэтому вместо Nums['ONE'] используется синтаксис Nums.ONE.

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

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

6 голосов
/ 10 июня 2013

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

...