Як створити константу в Python?


988

Чи є спосіб оголосити константу в Python? У Java ми можемо створювати постійні значення таким чином:

public static final String CONST_NAME = "Name";

Який еквівалент вищезазначеної константної декларації Java в Python?


6
насправді спосіб створення змінних лише для читання можливий через функцію / декоратор властивості python . відповідь на і є прикладом для користувача використання цього. властивість більш загальновикористовується, ніж це, але хороший аналіз того, як це працює, є на атрибутах і методах Python Shalabh Chaturvedi .
n611x007

20
ІМХО, впроваджувати сталість "не пітонічно". У Python 2.7 ви навіть можете писати True=False, а потім (2+2==4)==Trueповертатися False.
оса

8
Як свідчать інші відповіді, немає можливості декларувати константи не можна або не потрібно. Але ви можете прочитати цей ПЕП про умовності. напр. THIS_IS_A_CONSTANT
Перера

34
@osa: Ви не можете цього зробити в python 3 - SyntaxError: can't assign to keyword. Це здається гарною справою.
naught101

3
Дивно, що це досі не згадувалося, але Enums здасться хорошим способом визначення перелічених констант.
cs95

Відповіді:


973

Ні, немає. Ви не можете оголосити змінну чи значення постійними в Python. Просто не міняйте цього.

Якщо ви перебуваєте в класі, еквівалент буде:

class Foo(object):
    CONST_NAME = "Name"

якщо ні, то просто

CONST_NAME = "Name"

Але ви можете поглянути на фрагмент коду Константи в Python від Алекса Мартеллі.


Станом на Python 3.8, є typing.Finalзмінна анотація, яка повідомляє перевіряючим статичні типи (наприклад, mypy), що змінна не повинна бути переназначена. Це найближчий еквівалент Java final. Однак це фактично не перешкоджає призначенню :

from typing import Final

a: Final = 1

# Executes fine, but mypy will report an error if you run mypy on this:
a = 2

27
Замість того, щоб робити те, що є в "Константи в Python", ви повинні використовувати функцію "властивості" або декоратор.
Сет Джонсон

26
Люди запитують про таку саму особливість у Perl. Існує модуль імпорту, який називається "константа використання", але (AFAIK) це просто обгортка для створення крихітної функції, яка повертає значення. Я роблю те саме в Python. Приклад:def MY_CONST_VALUE(): return 123
kevinarpe

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

108
Просто не міняйте цього. ти зробив мій день
Привіт-Ангел

89
"Просто не змінюй" це зовсім не корисно. Він не відповідає на питання, і я б запропонував його зняти.
Bartek Banachewicz

354

Немає constключового слова, як в інших мовах, проте можна створити властивість, яка має "функцію отримання" для зчитування даних, але немає "функції встановлення" для повторного запису даних. Це по суті захищає ідентифікатор від зміни.

Ось альтернативна реалізація з використанням властивості класу:

Зауважте, що код далеко не простий для читача, який цікавиться константами. Дивіться пояснення нижче

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

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

CONST = _Const()

print CONST.FOO
##3131964110

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

Пояснення коду:

  1. Визначте функцію, constantяка приймає вираз, і використовує її для побудови "гетьтера" - функції, яка виключно повертає значення виразу.
  2. Функція сеттера піднімає 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. Скористайтеся тим, що ми вибрали саме ці назви для власного getter & setter і створимо словник для ключових слів, використовуючи ** (подвійну зірочку), застосовану до всіх локальних визначень цього діапазону для передачі параметрів propertyфункції

11
Виходячи з документації на, AttributeErrorі TypeErrorя вважаю, що піднятим винятком повинна бути нова помилка, яку я пропоную називати ConstantErrorабо щось подібне, що є підкласом TypeError. Розділ у документах змушує мене думати, що: docs.python.org/2/library/exceptions.html
ArtOfWarfare

3
Я здивований цим кодом. Чому в якості аргументу шрифт методів FOO () та BAR () є self? Мій IDE підкреслює дужки червоним кольором (помилка "компілювати"). Я втомився ставити себе в це, але тоді я отримую помилку.
користувач3770060

10
Досягнувши цих довжин, очевидний явний недолік мови python. Чому вони не відчули необхідності додавати це в Python 3. Я не можу повірити, що ніхто не запропонував цього, і я просто не можу бачити логіку того, що якийсь комітет йде, так, константи? ні.
Andrew S

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 (як правило).

Ви також можете використовувати nametuple для створення констант:

>>> 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

17
Виконувати 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()як константу. Звичайно, це. Але це добре і дуже відповідає принципу пітона "Всі ми тут дорослі" - тобто розробнику рідко буде заборонено робити рішення про перебір правила, коли у них є вагомі причини та знають, що вони роблять.
jonathan.scholbach

69

Нещодавно я знайшов дуже коротке оновлення до цього, яке автоматично збільшує змістовні повідомлення про помилки та перешкоджає доступу через __dict__:

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

CONST = CONST()

# ----------

print(CONST.FOO)    # 1234

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

Ми визначаємо над собою, як зробити себе екземпляром, а потім використовуємо слоти, щоб гарантувати, що додаткові атрибути не можуть бути додані. Це також видаляє __dict__маршрут доступу. Звичайно, весь об’єкт ще можна переробити.

Правка - оригінальне рішення

Я, мабуть, пропускаю хитрість тут, але це, здається, працює для мене:

class CONST(object):
    FOO = 1234

    def __setattr__(self, *_):
        pass

CONST = CONST()

#----------

print CONST.FOO    # 1234

CONST.FOO = 4321
CONST.BAR = 5678

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

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

Це загальний біль за одне значення, але ви можете прикріпити багато до свого CONSTоб'єкта. Маючи вищий клас, назва класу також здається трохи громіздкою, але я думаю, що це в цілому досить лаконічно.


11
Це найкраща і найясніша відповідь, оскільки вона має найменший «механізм», але найбільш функціональний. Підвищення винятку важливо, хоча ... не варіант.
Ерік Аронесті

Я розробив коротший маршрут, який автоматично створює значущі помилки, але майже в одному стилі. Оригінальну ідею я залишив тут для порівняння.
Джон Беттс

Як шкода, що вам ще потрібен цей CONST.префікс. Також у мультимодульних ситуаціях це стане складним.
Альфа

1
Я думаю, що ти зазвичай хочеш згрупувати константи в якісь пов'язані зв'язки в будь-якій ситуації (а не мати один гігантський об'єкт CONST), тому це, мабуть, не так вже й погано.
Джон Беттс

Чому ця відповідь досі так далеко вниз ?!__slots__Рішення є настільки елегантним і ефективним. З усього, що я прочитав, це приблизно наближене до створення констант в Python. Дуже дякую. І для всіх, хто цікавиться, ось блискуче і глибоке пояснення __slots__магії.
ДжонГалт

34

У Python немає констант.

Мабуть, найпростіша альтернатива - визначити функцію для неї:

def MY_CONSTANT():
    return 42

MY_CONSTANT() тепер є весь функціонал постійного (плюс деякі дратівливі брекети).


1
Я просто хотів додати цю пропозицію, але, на щастя, я прокрутився до низько оцінених відповідей. Я сподіваюсь, що це буде надано більше уваги, і я повністю погоджуюсь, що він має всі функціональні можливості постійного рівня, і це дуже просто і прямо. Дивлячись на кількість кодового коду у всіх складних рішеннях, я вважаю брекети відносно неприємними.
yaccob

1
це найпростіша відповідь, хоча слід зазначити, що вона має деяку накладну вартість і не зупинить ідіотів, що змінюють повернене значення. Це просто завадить коду далі вниз по лінії змінити джерело
MrMesees

@MrMesees змінює повернене значення? Ви маєте на увазі редагування джерела? Але від цього ви не захищені навіть у C ++, де константи (як constexpr) - справжні жорсткі константи.
Руслан

@Ruslan, що я мав на увазі, що оскільки python не має constexpr, це не зупинить редагування значення після повернення його у зовнішній контекст. 42 не було зроблено нічого, щоб застосувати заморожений стан у цьому прикладі.
MrMesees

20

На додаток до двох головних відповідей (просто використовуйте змінні з іменами UPPERCASE або використовуйте властивості, щоб зробити значення лише для читання), хочу зазначити, що можна використовувати метакласи для реалізації названих констант. Я пропоную дуже просте рішення, використовуючи метакласи на GitHub, які можуть бути корисними, якщо ви хочете, щоб значення були більш інформативними щодо їх типу / імені:

>>> from named_constants import Constants
>>> class Colors(Constants):
...     black = 0
...     red = 1
...     white = 15
...
>>> c = Colors.black
>>> c == 0
True
>>> c
Colors.black
>>> c.name()
'black'
>>> Colors(0) is c
True

Це дещо досконаліший Python, але все ще дуже простий у використанні та зручний. (Модуль має ще деякі функції, зокрема константи, які доступні лише для читання, дивіться його README.)

Існують подібні рішення, що пливуть навколо в різних сховищах, але, наскільки мені відомо, їм або не вистачає однієї з основних характеристик, яких я б очікував від констант (на кшталт того, що вони є постійними або мають довільний тип), або вони мають езотеричні особливості, додавши, що роблять їх менш загальноприйнятими. Але YMMV, я буду вдячний за відгуки. :-)


3
Мені подобається ваша реалізація на GitHub. Я майже був готовий написати базовий клас, який реалізував функцію зворотного пошуку, але я бачу, що ви це зробили та багато іншого!
Керр

Дякую, @Kerr, це перший відгук, який я отримав і зробив мене щасливим. :-)
hans_meine

Дивовижно. Я просто спробував це. Приємно мати це як варіант. Хоча я і не вирішив, чи мені достатньо піклуватися про аспект лише для читання, використовувати це, а не просто робити def enum(**enums): return type('Enum', (), enums). Numbers = enum(ONE=1, TWO=2, THREE='three'), відповідно до stackoverflow.com/a/1695250/199364 , розділ "У попередніх версіях ..."
ToolmakerSteve

19

Властивості - це один із способів створення констант. Це можна зробити, оголосивши властивість getter, але ігноруючи сетер. Наприклад:

class MyFinalProperty(object):

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

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


За ціновим рішенням. Я просто реалізував це, знайшовши цю сторінку (не цю відповідь) і обернувшись назад, щоб додати її, якщо ще не. Я хотів підкреслити корисність цієї відповіді.
Марк

18

Редагування: Доданий зразок коду для Python 3

Примітка: ця інша відповідь виглядає так, що забезпечує набагато повнішу реалізацію, подібну до наступної (з більшою кількістю функцій).

Спочатку складіть метаклас :

class MetaConst(type):
    def __getattr__(cls, key):
        return cls[key]

    def __setattr__(cls, key, value):
        raise TypeError

Це запобігає зміні властивостей статики. Потім складіть ще один клас, який використовує цей метаклас:

class Const(object):
    __metaclass__ = MetaConst

    def __getattr__(self, name):
        return self[name]

    def __setattr__(self, name, value):
        raise TypeError

Або якщо ви використовуєте Python 3:

class Const(object, metaclass=MetaConst):
    def __getattr__(self, name):
        return self[name]

    def __setattr__(self, name, value):
        raise TypeError

Це повинно запобігти зміні реквізитів екземпляра. Щоб використовувати його, успадкуйте:

class MyConst(Const):
    A = 1
    B = 2

Тепер реквізит, до якого можна отримати доступ безпосередньо або через екземпляр, повинен бути постійним:

MyConst.A
# 1
my_const = MyConst()
my_const.A
# 1

MyConst.A = 'changed'
# TypeError
my_const.A = 'changed'
# TypeError

Ось приклад вище в дії. Ось ще один приклад для Python 3.


10

Ви можете використовувати nametuple як вирішення для ефективного створення константи, яка працює так само, як статична остаточна змінна на Java ("константа Java"). По мірі подолання обстановки це щось елегантно. (Більш елегантним підходом було б просто вдосконалити мову Python --- яку мову ви можете переосмислитиmath.pi ? - але я відхиляюся.)

(Коли я це пишу, я усвідомлюю ще одну відповідь на це питання, згадане з іменем nametuple, але я продовжу тут, тому що я покажу синтаксис, який більш точно паралельний тому, що ви очікували в Java, оскільки немає необхідності створювати ім'я введіть, як nametuple змушує вас робити.)

Наслідуючи ваш приклад, ви пам’ятаєте, що в Java ми повинні визначати константу всередині якогось класу ; оскільки ви не згадали назву класу, назвемо це Foo. Ось клас Java:

public class Foo {
  public static final String CONST_NAME = "Name";
}

Ось еквівалент Python.

from collections import namedtuple
Foo = namedtuple('_Foo', 'CONST_NAME')('Name')

Ключовий момент, який я хочу тут додати, - це те, що вам не потрібен окремий Fooтип ("анонімний ім'я з кортежем" було б добре, навіть якщо це звучить як оксиморон), тому ми називаємо наш названий пар, _Fooтак що, сподіваємось, він не буде перехід до імпорту модулів.

Другий момент тут полягає в тому, що ми негайно створюємо екземпляр nametuple, називаючи його Foo; не потрібно робити це окремим кроком (якщо ви цього не хочете). Тепер ви можете робити те, що ви можете зробити на Java:

>>> Foo.CONST_NAME
'Name'

Але ви не можете призначити це:

>>> Foo.CONST_NAME = 'bar'

AttributeError: can't set attribute

Підтвердження: я думав, що я винайшов підхід з назвою "дует", але потім бачу, що хтось ще дав подібну (хоча і менш компактну) відповідь. Потім я також помітив, що в Python називають "кортежами"? , що вказує на цеsys.version_info зараз це іменований пар, тому, можливо, стандартна бібліотека Python вже придумала цю ідею набагато раніше.

Зауважте, що, на жаль (це все ще Python), ви можете повністю стерти все Fooзавдання:

>>> Foo = 'bar'

(facepalm)

Але принаймні ми не заважаємо Foo.CONST_NAMEзмінювати значення, і це краще, ніж нічого. Удачі.


Дякуємо за те, що ви знайшли підхід з ім'ям. Безумовно інноваційний. Ви також можете знайти тут мій "коментар" відповідним.
RayLuo

10

PEP 591 має "остаточний" класифікатор. Забезпечення виконання - до перевірки типу.

Отже, ви можете зробити:

MY_CONSTANT: Final = 12407

Примітка: Final ключове слово застосовується лише для версії Python 3.8


9

Ось реалізація класу "Константи", яка створює екземпляри з атрибутами лише для читання (постійними). Напр. Можна використовувати Nums.PIдля отримання значення, яке було ініціалізовано як 3.14159, і Nums.PI = 22викликає виняток.

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

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

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

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

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

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

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

Завдяки FrozenDict @MikeGraham , який я використав як вихідний пункт. Змінено, тож замість Nums['ONE']синтаксису використання є Nums.ONE.

І завдяки відповіді @ Рауфіо, за ідею перекрити __ setattr __.

Або для реалізації з більшою функціональністю, див. Ім'я @constants @Hans_meine на GitHub


2
Пітон - це мова, яка дає згоду дорослим. Немає захисту від подібного. Nums._d['PI'] = 22 Я вважаю, що сама мова не дає жодного способу позначати речі як незмінні.
Аджай М

8

Кортеж технічно кваліфікується як константа, оскільки кортеж призведе до помилки, якщо ви спробуєте змінити одне з його значень. Якщо ви хочете оголосити кортеж з одним значенням, то поставте кому після єдиного його значення, наприклад:

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

Щоб перевірити значення цієї змінної, використовуйте щось подібне до цього:

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

Якщо ви спробуєте змінити це значення, з’явиться помилка.


7

Я б створив клас, який переосмислює __setattr__метод класу базового об'єкта і обертає мої константи цим, зауважте, що я використовую python 2.7:

class const(object):
    def __init__(self, val):
        super(const, self).__setattr__("value", val)
    def __setattr__(self, name, val):
        raise ValueError("Trying to change a constant value", self)

Щоб обмотати рядок:

>>> constObj = const("Try to change me")
>>> constObj.value
'Try to change me'
>>> constObj.value = "Changed"
Traceback (most recent call last):
   ...
ValueError: Trying to change a constant value
>>> constObj2 = const(" or not")
>>> mutableObj = constObj.value + constObj2.value
>>> mutableObj #just a string
'Try to change me or not'

Це досить просто, але якщо ви хочете використовувати свої константи так само, як і непостійний об’єкт (без використання constObj.value), це буде трохи інтенсивніше. Цілком можливо, що це може спричинити проблеми, тому, можливо, найкраще продовжувати .valueпоказувати і знати, що ви робите операції з константами (можливо, це не самий «пітонічний» спосіб).


+1 за цікавий підхід. Хоча це не так чисто, як відповіді, які вже були надані. І навіть найпростіший раніше запропонований варіант def ONE(): return 1простіший у використанні, ONE()ніж ця відповідь ONE.value.
ToolmakerSteve

7

На жаль, у Python поки що немає констант, і це соромно. ES6 вже додав константи підтримки до JavaScript ( https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/const ), оскільки це дуже корисна річ у будь-якій мові програмування. Як було сказано в інших відповідях у спільноті Python, використовуйте змінну верхнього регістру convention - user як константи, але це не захищає від довільних помилок у коді. Якщо вам подобається, вам може бути корисно однофайлове рішення наступним чином (див. Документації, як ним користуватися).

файл constants.py

import collections


__all__ = ('const', )


class Constant(object):
    """
    Implementation strict constants in Python 3.

    A constant can be set up, but can not be changed or deleted.
    Value of constant may any immutable type, as well as list or set.
    Besides if value of a constant is list or set, it will be converted in an immutable type as next:
        list -> tuple
        set -> frozenset
    Dict as value of a constant has no support.

    >>> const = Constant()
    >>> del const.temp
    Traceback (most recent call last):
    NameError: name 'temp' is not defined
    >>> const.temp = 1
    >>> const.temp = 88
    Traceback (most recent call last):
        ...
    TypeError: Constanst can not be changed
    >>> del const.temp
    Traceback (most recent call last):
        ...
    TypeError: Constanst can not be deleted
    >>> const.I = ['a', 1, 1.2]
    >>> print(const.I)
    ('a', 1, 1.2)
    >>> const.F = {1.2}
    >>> print(const.F)
    frozenset([1.2])
    >>> const.D = dict()
    Traceback (most recent call last):
        ...
    TypeError: dict can not be used as constant
    >>> del const.UNDEFINED
    Traceback (most recent call last):
        ...
    NameError: name 'UNDEFINED' is not defined
    >>> const()
    {'I': ('a', 1, 1.2), 'temp': 1, 'F': frozenset([1.2])}
    """

    def __setattr__(self, name, value):
        """Declaration a constant with value. If mutable - it will be converted to immutable, if possible.
        If the constant already exists, then made prevent againt change it."""

        if name in self.__dict__:
            raise TypeError('Constanst can not be changed')

        if not isinstance(value, collections.Hashable):
            if isinstance(value, list):
                value = tuple(value)
            elif isinstance(value, set):
                value = frozenset(value)
            elif isinstance(value, dict):
                raise TypeError('dict can not be used as constant')
            else:
                raise ValueError('Muttable or custom type is not supported')
        self.__dict__[name] = value

    def __delattr__(self, name):
        """Deny against deleting a declared constant."""

        if name in self.__dict__:
            raise TypeError('Constanst can not be deleted')
        raise NameError("name '%s' is not defined" % name)

    def __call__(self):
        """Return all constans."""

        return self.__dict__


const = Constant()


if __name__ == '__main__':
    import doctest
    doctest.testmod()

Якщо цього недостатньо, перегляньте повний тест.

import decimal
import uuid
import datetime
import unittest

from ..constants import Constant


class TestConstant(unittest.TestCase):
    """
    Test for implementation constants in the Python
    """

    def setUp(self):

        self.const = Constant()

    def tearDown(self):

        del self.const

    def test_create_constant_with_different_variants_of_name(self):

        self.const.CONSTANT = 1
        self.assertEqual(self.const.CONSTANT, 1)
        self.const.Constant = 2
        self.assertEqual(self.const.Constant, 2)
        self.const.ConStAnT = 3
        self.assertEqual(self.const.ConStAnT, 3)
        self.const.constant = 4
        self.assertEqual(self.const.constant, 4)
        self.const.co_ns_ta_nt = 5
        self.assertEqual(self.const.co_ns_ta_nt, 5)
        self.const.constant1111 = 6
        self.assertEqual(self.const.constant1111, 6)

    def test_create_and_change_integer_constant(self):

        self.const.INT = 1234
        self.assertEqual(self.const.INT, 1234)
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.INT = .211

    def test_create_and_change_float_constant(self):

        self.const.FLOAT = .1234
        self.assertEqual(self.const.FLOAT, .1234)
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.FLOAT = .211

    def test_create_and_change_list_constant_but_saved_as_tuple(self):

        self.const.LIST = [1, .2, None, True, datetime.date.today(), [], {}]
        self.assertEqual(self.const.LIST, (1, .2, None, True, datetime.date.today(), [], {}))

        self.assertTrue(isinstance(self.const.LIST, tuple))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.LIST = .211

    def test_create_and_change_none_constant(self):

        self.const.NONE = None
        self.assertEqual(self.const.NONE, None)
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.NONE = .211

    def test_create_and_change_boolean_constant(self):

        self.const.BOOLEAN = True
        self.assertEqual(self.const.BOOLEAN, True)
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.BOOLEAN = False

    def test_create_and_change_string_constant(self):

        self.const.STRING = "Text"
        self.assertEqual(self.const.STRING, "Text")

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.STRING += '...'

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.STRING = 'TEst1'

    def test_create_dict_constant(self):

        with self.assertRaisesRegexp(TypeError, 'dict can not be used as constant'):
            self.const.DICT = {}

    def test_create_and_change_tuple_constant(self):

        self.const.TUPLE = (1, .2, None, True, datetime.date.today(), [], {})
        self.assertEqual(self.const.TUPLE, (1, .2, None, True, datetime.date.today(), [], {}))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.TUPLE = 'TEst1'

    def test_create_and_change_set_constant(self):

        self.const.SET = {1, .2, None, True, datetime.date.today()}
        self.assertEqual(self.const.SET, {1, .2, None, True, datetime.date.today()})

        self.assertTrue(isinstance(self.const.SET, frozenset))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.SET = 3212

    def test_create_and_change_frozenset_constant(self):

        self.const.FROZENSET = frozenset({1, .2, None, True, datetime.date.today()})
        self.assertEqual(self.const.FROZENSET, frozenset({1, .2, None, True, datetime.date.today()}))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.FROZENSET = True

    def test_create_and_change_date_constant(self):

        self.const.DATE = datetime.date(1111, 11, 11)
        self.assertEqual(self.const.DATE, datetime.date(1111, 11, 11))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.DATE = True

    def test_create_and_change_datetime_constant(self):

        self.const.DATETIME = datetime.datetime(2000, 10, 10, 10, 10)
        self.assertEqual(self.const.DATETIME, datetime.datetime(2000, 10, 10, 10, 10))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.DATETIME = None

    def test_create_and_change_decimal_constant(self):

        self.const.DECIMAL = decimal.Decimal(13123.12312312321)
        self.assertEqual(self.const.DECIMAL, decimal.Decimal(13123.12312312321))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.DECIMAL = None

    def test_create_and_change_timedelta_constant(self):

        self.const.TIMEDELTA = datetime.timedelta(days=45)
        self.assertEqual(self.const.TIMEDELTA, datetime.timedelta(days=45))

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.TIMEDELTA = 1

    def test_create_and_change_uuid_constant(self):

        value = uuid.uuid4()
        self.const.UUID = value
        self.assertEqual(self.const.UUID, value)

        with self.assertRaisesRegexp(TypeError, 'Constanst can not be changed'):
            self.const.UUID = []

    def test_try_delete_defined_const(self):

        self.const.VERSION = '0.0.1'
        with self.assertRaisesRegexp(TypeError, 'Constanst can not be deleted'):
            del self.const.VERSION

    def test_try_delete_undefined_const(self):

        with self.assertRaisesRegexp(NameError, "name 'UNDEFINED' is not defined"):
            del self.const.UNDEFINED

    def test_get_all_defined_constants(self):

        self.assertDictEqual(self.const(), {})

        self.const.A = 1
        self.assertDictEqual(self.const(), {'A': 1})

        self.const.B = "Text"
        self.assertDictEqual(self.const(), {'A': 1, 'B': "Text"})

Переваги: ​​1. Доступ до всіх констант для цілого проекту 2. Суворий контроль за значеннями констант

Недоліки: 1. Не підтримує спеціальні типи та тип 'dict'

Примітки:

  1. Тестований з Python3.4 та Python3.5 (я використовую для нього "токсик")

  2. Тестове середовище:

.

$ uname -a
Linux wlysenko-Aspire 3.13.0-37-generic #64-Ubuntu SMP Mon Sep 22 21:28:38 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

Ви можете це трохи покращити, автоматично перетворюючи словники на названі кортежі
Пітер Шорн

6

Пітонічний спосіб оголошення "константи" - це в основному змінна рівня модуля:

RED = 1
GREEN = 2
BLUE = 3

А потім запишіть свої класи чи функції. Оскільки константи майже завжди є цілими числами, і вони також незмінні в Python, у вас є дуже мало шансів змінити це.

Якщо, звичайно, якщо ви чітко налаштовані RED = 2.


21
Так, але блокування можливості "явно встановити RED = 2" - це вся перевага (іншими мовами) - можливість оголосити ім'я змінної "постійним"!
ToolmakerSteve

6
Чи отримали б ви користь від блокування цього? Найбільш корисна річ у const - це зазвичай оптимізація компілятора, яка насправді не є річчю в Python. Хочете щось бути постійним? Просто не міняйте цього. Якщо ви турбуєтесь про те, щоб хтось змінив це, ви можете просто поставити його поза їх рамки або просто зрозуміти, що якщо хтось це змінює, це їхня проблема, і їм потрібно боротися з цим, а не ви.
Кевін

@Kevin: " Чи отримали б ви користь ... ", вигода staticмати єдине сховище для значення для всіх примірників класу? Якщо тільки немає можливості оголосити статичну змінну / клас дійсно.
хв.

8
Основна проблема полягає в тому, що деякі можуть бачити це як значення, яке є джерелом істини, не може бути змінене, і використовувати його як джерело істини протягом усього коду замість того, щоб вводити магічні значення (яких я бачу багато в Python) - і інші можуть сприймати це як щось, що їм дозволяється змінювати за бажанням. Коли хтось змінює глобальну змінну, і ви не можете сказати, де вона змінилася, і програма виходить з ладу, тому що RED = "синій" замість "червоного", ви представляєте абсолютно непотрібну проблему, яка вже вирішена так просто і є загальновизнаним.
Dagrooms

5

Ми можемо створити об’єкт дескриптора.

class Constant:
  def __init__(self,value=None):
    self.value = value
  def __get__(self,instance,owner):
    return self.value
  def __set__(self,instance,value):
    raise ValueError("You can't change a constant")

1) Якщо ми хотіли працювати з константами на рівні екземпляра, тоді:

class A:
  NULL = Constant()
  NUM = Constant(0xFF)

class B:
  NAME = Constant('bar')
  LISTA = Constant([0,1,'INFINITY'])

>>> obj=A()
>>> print(obj.NUM)  #=> 255
>>> obj.NUM =100

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: You can't change a constant

2) якби ми хотіли створювати константи лише на рівні класу, ми могли б використовувати метаклас, який служить контейнером для наших констант (наш дескриптор); всі класи, що спадають, успадкують наші константи (наші дескрипторні об'єкти) без будь-якого ризику, який можна змінити.

# metaclass of my class Foo
class FooMeta(type): pass

# class Foo
class Foo(metaclass=FooMeta): pass

# I create constants in my metaclass
FooMeta.NUM = Constant(0xff)
FooMeta.NAME = Constant('FOO')

>>> Foo.NUM   #=> 255
>>> Foo.NAME  #=> 'FOO'
>>> Foo.NUM = 0 #=> ValueError: You can't change a constant

Якщо я створять підклас Foo, цей клас успадкує константу без можливості їх зміни

class Bar(Foo): pass

>>> Bar.NUM  #=> 255
>>> Bar.NUM = 0  #=> ValueError: You can't change a constant

4

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

>>> constants = {"foo":1, "bar":2}
>>> print constants
{'foo': 1, 'bar': 2}
>>> constants["bar"] = 3
>>> print constants
{'foo': 1, 'bar': 3}

4

Ось хитрість, якщо вам потрібно константи і не піклуються про їх значення:

Просто визначте порожні класи.

наприклад:

class RED: 
    pass
class BLUE: 
    pass

4

У python константа - це просто змінна з назвою у всіх великих літерах, зі словами, розділеними символом підкреслення,

напр

DAYS_IN_WEEK = 7

Значення є змінним, оскільки в ньому ви можете змінити його. Але з огляду на те, що правила для імені говорять вам про постійне, чому б ви зробили це? Я маю на увазі, це ваша програма зрештою!

Такий підхід застосовується протягом усього пітона. З privateтієї ж причини немає ключового слова. Префікс імені підкресленням, і ви знаєте, що воно має бути приватним. Код може порушити правило .... так само, як програміст все одно може видалити приватне ключове слово.

Python міг би додати а const ключове слово ... але програміст може видалити ключове слово, а потім змінити константу, якщо вони хочуть, але навіщо це робити? Якщо ви хочете порушити правило, ви можете все-таки змінити правило. Але навіщо турбуватися порушувати правило, якщо ім'я чітко виражає наміри?

Можливо, є якийсь одиничний тест, де є сенс застосувати зміну до значення? Щоб побачити, що відбувається протягом 8-денного тижня, навіть якщо в реальному світі кількість днів за тиждень змінити не можна. Якщо мова перестала робити виняток, якщо є лише цей один випадок, вам потрібно порушити правило ... вам тоді доведеться припинити оголошувати його як константу, хоча вона все ще є константою в додатку, і є тільки цей тестовий випадок, який бачить, що станеться, якщо його змінити.

Увесь верхній регістр називає, що він повинен бути постійним. Саме це важливо. Не мова, що змушує обмеження коду, ви все одно можете змінити.

Така філософія пітона.


4

Немає ідеального способу зробити це. Як я розумію, більшість програмістів просто використовуватиме велику ідентифікатор, тому PI = 3.142 можна легко зрозуміти як постійну.

З іншого боку, якщо ви хочете щось, що насправді діє як константа, я не впевнений, що ви його знайдете. З будь-чим, що ви робите, завжди буде якийсь спосіб редагування "константи", тому він насправді не буде постійним. Ось дуже простий, брудний приклад:

def define(name, value):
  if (name + str(id(name))) not in globals():
    globals()[name + str(id(name))] = value

def constant(name):
  return globals()[name + str(id(name))]

define("PI",3.142)

print(constant("PI"))

Це виглядає так, що це зробить постійним стиль PHP.

Насправді все, що потрібно, щоб хтось змінив значення, це таке:

globals()["PI"+str(id("PI"))] = 3.1415

Це те саме, що і для всіх інших рішень, які ви знайдете тут - навіть розумні, які складають клас та переосмислюють метод набору атрибутів, - завжди знайдеться шлях до них. Ось так і є Python.

Моя рекомендація - просто уникати всіх клопотів і просто використовувати свої ідентифікатори. Це насправді не було б належною постійною, але тоді знову нічого не буде.


4

Існує більш чистий спосіб зробити це за допомогою namestuple:

from collections import namedtuple


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

Приклад використання

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

З таким точно підходом ви можете називати простір імен своїх констант.


Для всіх, хто читає це, майте на увазі, що якщо ви встановите змінений об’єкт як одну з цих констант, кожен може змінити його внутрішнє значення. наприклад, давайте bar = [1, 2, 3], тоді ви можете зробити так: CONSTS.bar [1] = 'a', і він не буде відхилений. Тож будьте обережні з цього приводу.
Хуан Ігнасіо Санчес

Замість цього придурливого методу, який я створив просто для розваги, рекомендую використовувати замість цього декоратор властивостей Python.
Хуан Ігнасіо Санчес

4

Можливо, вам допоможе бібліотека pconst ( github ).

$ pip install pconst

from pconst import const
const.APPLE_PRICE = 100
const.APPLE_PRICE = 200

[Out] Constant value of "APPLE_PRICE" is not editable.


3

Ви можете використовувати StringVar або IntVar і т. Д., Ваша константа const_val

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

def reverse(*args):
    const_val.set(val)

2

Ви можете зробити це з collections.namedtupleі itertools:

import collections
import itertools
def Constants(Name, *Args, **Kwargs):
  t = collections.namedtuple(Name, itertools.chain(Args, Kwargs.keys()))
  return t(*itertools.chain(Args, Kwargs.values()))

>>> myConstants = Constants('MyConstants', 'One', 'Two', Three = 'Four')
>>> print myConstants.One
One
>>> print myConstants.Two
Two
>>> print myConstants.Three
Four
>>> myConstants.One = 'Two'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute

2

(Цей абзац мав на увазі коментар до тих відповідей тут і там , у яких згадувалося namedtuple, але він стає занадто довгим, щоб вписатись у коментар. Отже, ось він іде.)

Зазначений вище підхід, визначений вище, безумовно, є інноваційним. Для повноти, хоча в кінці розділу NamedTuple в офіційній документації він читає:

перераховані константи можуть бути реалізовані з названими кортежами, але простіше і ефективніше використовувати просту декларацію класу:

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

Іншими словами, офіційна документація надає перевагу використовувати практичний спосіб, а не реалізувати поведінку лише для читання. Я думаю, це стає ще одним прикладом дзен Python :

Просте - краще, ніж складне.

практичність б’є чистоту.


2

Ось це збірка ідіом, яку я створив як спробу вдосконалити деякі вже доступні відповіді.

Я знаю, що використання константи не є пітонічним, і ви не повинні робити цього вдома!

Однак Python - така динамічна мова! Цей форум показує, як можна створювати конструкції, схожі на константи. Ця відповідь має основну мету дослідити те, що можна виразити мовою.

Будь ласка, не будьте дуже суворі зі мною :-).

Для більш детальної інформації я написав супровідний блог про ці ідіоми .

У цій публікації я буду називати константну змінну до постійної посилання на значення (незмінні чи інші). Більше того, я кажу, що змінна має заморожене значення, коли вона посилається на об'єкт, що змінюється, що клієнтський код не може оновити свої значення.

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

Ця ідіома створює те, що схоже на простір імен постійних змінних (він же SpaceConstants). Це модифікація фрагмента коду від Алекса Мартеллі, щоб уникнути використання модульних об'єктів. Зокрема, ця модифікація використовує то , що я називаю клас фабрики , тому що в межах SpaceConstants функції, клас називається SpaceConstants визначається, і його примірник повертається.

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

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

sc = SpaceConstants()

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

Простір заморожених значень (SpaceFrozenValues)

Наступна ідіома - це модифікація SpaceConstants, де заморожені змінні об'єкти заморожені. Ця реалізація використовує те, що я називаю спільним закриттям між функціями setattr та getattr . Значення об'єкта, що змінюється, копіюється та посилається на кеш змінної задачі всередині закритого функціонування функції. Він утворює те, що я називаю захищеною від закриття копією об'єкта, що змінюється .

Ви повинні бути обережними у використанні цієї ідіоми, оскільки getattr повертає значення кешу, роблячи глибоку копію. Ця операція може мати значний вплив на продуктивність на великих об'єктах!

from copy import deepcopy

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

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

Постійний простір (ConstantSpace)

Ця ідіома є незмінним простором імен постійних змінних або ConstantSpace . Це поєднання дивовижно простої відповіді Джона Беттса в стаковому потоці з фабрикою класів .

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

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

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

Заморожений простір (FrozenSpace)

Ця ідіома є незмінним простором імен заморожених змінних або FrozenSpace . Він отримується з попереднього шаблону, роблячи кожну змінну захищеною властивістю шляхом закриття згенерованого класу FrozenSpace .

from copy import deepcopy

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

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

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

print(fs.x) # print "2"
fs.x = 3 # raise "AttributeError: 'FrozenSpace' object attribute 'x' is read-only"
print(fs.y) # print "{'name': 'y', 'value': 2}"
fs.y['name'] = 'yprime' # try to change mutable object
print(fs.y) # print "{'name': 'y', 'value': 2}"
fs.y = {} # raise "AttributeError: 'FrozenSpace' object attribute 'x' is read-only"
fs.z = 3 # raise "AttributeError: 'FrozenSpace' object has no attribute 'z'"

2

У Python константи не існують, але ви можете вказати, що змінна є константою і її не слід змінювати, додавши CONST_до початку імені змінної та заявивши, що вона є константою в коментарі:

myVariable = 0
CONST_daysInWeek = 7    # This is a constant - do not change its value.   
CONSTANT_daysInMonth = 30 # This is also a constant - do not change this value.

Крім того, ви можете створити функцію, яка діє як константа:

def CONST_daysInWeek():
    return 7;

1

У моєму випадку мені знадобилися незмінні байтарі для реалізації криптобібліотеки, що містить безліч буквальних чисел, які я хотів переконатися, що вони є постійними.

Ця відповідь спрацьовує, але спроба переназначення елементів bytearray не викликає помилок.

def const(func):
    '''implement const decorator'''
    def fset(self, val):
        '''attempting to set a const raises `ConstError`'''
        class ConstError(TypeError):
            '''special exception for const reassignment'''
            pass

        raise ConstError

    def fget(self):
        '''get a const'''
        return func()

    return property(fget, fset)


class Consts(object):
    '''contain all constants'''

    @const
    def C1():
        '''reassignment to C1 fails silently'''
        return bytearray.fromhex('deadbeef')

    @const
    def pi():
        '''is immutable'''
        return 3.141592653589793

Константи незмінні, але постійне призначення байтаря виходить з ладу:

>>> c = Consts()
>>> c.pi = 6.283185307179586  # (https://en.wikipedia.org/wiki/Tau_(2%CF%80))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "consts.py", line 9, in fset
    raise ConstError
__main__.ConstError
>>> c.C1[0] = 0
>>> c.C1[0]
222
>>> c.C1
bytearray(b'\xde\xad\xbe\xef')

Більш потужний, простий і, можливо, навіть більш "пітонічний" підхід передбачає використання об'єктів перегляду пам'яті (буферних об'єктів у <= python-2.6).

import sys

PY_VER = sys.version.split()[0].split('.')

if int(PY_VER[0]) == 2:
    if int(PY_VER[1]) < 6:
        raise NotImplementedError
    elif int(PY_VER[1]) == 6:
        memoryview = buffer

class ConstArray(object):
    '''represent a constant bytearray'''
    def __init__(self, init):
        '''
        create a hidden bytearray and expose a memoryview of that bytearray for
        read-only use
        '''
        if int(PY_VER[1]) == 6:
            self.__array = bytearray(init.decode('hex'))
        else:
            self.__array = bytearray.fromhex(init)

        self.array = memoryview(self.__array)

    def __str__(self):
        return str(self.__array)

    def __getitem__(self, *args, **kwargs):
       return self.array.__getitem__(*args, **kwargs)

Призначення елемента ConstArray - це TypeError:

>>> C1 = ConstArray('deadbeef')
>>> C1[0] = 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'ConstArray' object does not support item assignment
>>> C1[0]
222

1

Я пишу util lib для python const: kkconst - підтримка pypi str, int, float, datetime

екземпляр поля const буде зберігати свою поведінку базового типу.

Наприклад:

from __future__ import print_function
from kkconst import (
    BaseConst,
    ConstFloatField,
)

class MathConst(BaseConst):
    PI = ConstFloatField(3.1415926, verbose_name=u"Pi")
    E = ConstFloatField(2.7182818284, verbose_name=u"mathematical constant")  # Euler's number"
    GOLDEN_RATIO = ConstFloatField(0.6180339887, verbose_name=u"Golden Ratio")

magic_num = MathConst.GOLDEN_RATIO
assert isinstance(magic_num, ConstFloatField)
assert isinstance(magic_num, float)

print(magic_num)  # 0.6180339887
print(magic_num.verbose_name)  # Golden Ratio

Детальніше про використання ви можете прочитати URL-адресу pypi : pypi або github


1

Ви можете обернути константу в масивному масиві, позначити лише те, що вона пише, та завжди викликати її за нульовим індексом.

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

звичайно, це лише захищає вміст нумеру, а не саму змінну "CONSTANT"; Ви все ще можете зробити:

CONSTANT = 'foo'

і CONSTANTзміниться, однак це дозволить швидко кинути TypeError, коли перший CONSTANT[0]сценарій пізніше буде викликаний у сценарії.

хоча ... я гадаю, якщо ви в якийсь момент змінили його на

CONSTANT = [1,2,3]

тепер ви більше не отримаєте TypeError. хмммм….

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.