web-dev-qa-db-ja.com

Pythonで定数を作成する方法

Pythonで定数を宣言する方法はありますか? Javaでは、このようにして定数値を作成できます。

public static final String CONST_NAME = "Name";

Pythonでの上記のJava定数宣言と同等のものは何ですか?

822
zfranciscus

いいえ、ありません。 Pythonでは、変数や値を定数として宣言することはできません。変更しないでください。

あなたがクラスにいるならば、同等物は以下のようになるでしょう:

class Foo(object):
    CONST_NAME = "Name"

そうでなければ、それだけです

CONST_NAME = "Name"

しかし、コードスニペット Pythonの定数 Alex Martelliによる/を見てみたいと思うかもしれません。

818
Felix Kling

他の言語のようなconstキーワードはありませんが、 データを読み取るための "/ getter関数"を持つプロパティを作成することは可能です /データを書き換えるための "setter関数" はありません。 これは本質的に識別子が変更されるのを防ぎます。

これは、クラスプロパティを使った別の実装です。

定数について疑問に思う読者にとって、コードは簡単ではないことに注意してください。以下の説明を参照してください

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を定義し、それを使用して "getter" - 式の値のみを返す関数 - を構築します。
  2. Setter関数はTypeErrorを送出するので読み取り専用です
  3. 先ほど作成したconstant関数を装飾として使用して、読み取り専用プロパティをすばやく定義します。

そして、他のもっと古風な方法で:

(コードはかなり扱いにくいので、以下に説明があります)

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

CONST = _Const()

print CONST.FOO
##3131964110

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

@applyデコレータは廃​​止予定のようです。

  1. 識別子FOOを定義するために、firsは2つの関数を定義します(fset、fget - 名前は私の選択です)。
  2. 次に、組み込みのproperty関数を使用して、 "set"または "get"になるオブジェクトを作成します。
  3. property関数の最初の2つのパラメータはfsetfgetという名前です。
  4. 我々自身のgetterとsetterにこれらのまさにその名前を選んだという事実を使い、そのスコープのすべてのローカル定義に適用される**(二重アスタリスク)を使ってキーワード辞書を作成し、property関数にパラメータを渡します。
318
inv

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
99
Anurag Uniyal

私は最近、これに対する非常に簡潔なアップデートを見つけました。これは自動的に意味のあるエラーメッセージを表示し、__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変数を設定しようとする試みを傍受することができます。必要に応じて、ここで例外をスローすることができます。クラス名を介してインスタンスをインスタンス化すると、クラスを介した直接アクセスが防止されます。

それは1つの値にとっては大変なことですが、CONSTオブジェクトにたくさん添付することができます。上流階級を持っているので、クラス名も少し不潔に見えますが、私はそれが全体的にかなり簡潔であると思います。

46
Jon Betts

Pythonには定数がありません。

おそらく最も簡単な方法は、それに対する関数を定義することです。

def MY_CONSTANT():
    return 42

MY_CONSTANT()は今や定数のすべての機能を持っています(それに加えていくつかの迷惑な中括弧)。

25
Saeed Baig

上の2つの答え(単に大文字の名前を持つ変数を使用するか、値を読み取り専用にするためのプロパティを使用する)に加えて、 named 定数を実装するためにメタクラスを使用することが可能です。私は 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を参照してください。)

さまざまなリポジトリには似たような解決策がありますが、私の知る限りでは定数から期待できる基本的な機能の1つ(定数である、任意の型であるなど)を欠いているか、難解な機能を追加しています。それらを一般的ではないものにします。しかし、YMMV、私はフィードバックに感謝します。 :-)

18
hans_meine

編集: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の別の例です。

16
doubleswirve

プロパティは定数を作成する1つの方法です。 getterプロパティを宣言することでそれを行うことができますが、setterは無視します。例えば:

class MyFinalProperty(object):

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

Pythonのプロパティを使うためのより多くの方法を見つけるために 私が書いた記事 を見ることができます。

11
nvd

これは、読み取り専用(定数)属性を持つインスタンスを作成する "Constants"クラスの実装です。例えば。 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 (__== "__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 __をオーバーライドするためのアイデアです。

あるいはもっと機能のある実装については、GitHubの@Hans_meineの named_constantsを見てください

8
ToolmakerSteve

残念ながら、Pythonにはまだ定数がありません。残念です。 ES6はすでにJavaScriptにサポート定数を追加しています( https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/const )。これはどのプログラミング言語でも非常に便利なことです。 Pythonコミュニティの他の回答で答えられているように、定数として慣例 - ユーザの大文字の変数を使用します、しかしそれはコードの任意のエラーに対して保護しません。あなたが好きなら、あなたは次のように単一ファイルの解決策が便利であると思われるかもしれません(docstringsの使い方を見てください)。

ファイル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 __== '__main__':
    import doctest
    doctest.testmod()

これだけでは足りない場合は、フルテストケースを参照してください。

import decimal
import uuid
import datetime
import unittest

from ..constants import Constant


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

    def setUp(self):

        self.const = Constant()

    def tearDown(self):

        del self.const

    def test_create_constant_with_different_variants_of_name(self):

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

    def test_create_and_change_integer_constant(self):

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

    def test_create_and_change_float_constant(self):

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

    def test_create_and_change_list_constant_but_saved_as_Tuple(self):

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

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

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

    def test_create_and_change_none_constant(self):

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

    def test_create_and_change_boolean_constant(self):

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

    def test_create_and_change_string_constant(self):

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

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

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

    def test_create_dict_constant(self):

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

    def test_create_and_change_Tuple_constant(self):

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

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

    def test_create_and_change_set_constant(self):

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

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

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

    def test_create_and_change_frozenset_constant(self):

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

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

    def test_create_and_change_date_constant(self):

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

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

    def test_create_and_change_datetime_constant(self):

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

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

    def test_create_and_change_decimal_constant(self):

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

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

    def test_create_and_change_timedelta_constant(self):

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

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

    def test_create_and_change_uuid_constant(self):

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

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

    def test_try_delete_defined_const(self):

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

    def test_try_delete_undefined_const(self):

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

    def test_get_all_defined_constants(self):

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

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

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

利点:1.プロジェクト全体のすべての定数へのアクセス2.定数の値に対する厳密な制御

欠けているもの:1.カスタムタイプとタイプ 'dict'をサポートしていません

ノート:

  1. Python3.4とPython3.5でテスト済み(私はそれのために 'tox'を使っています)

  2. テスト環境

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

基本オブジェクトクラスの__setattr__メソッドをオーバーライドするクラスを作成し、それに定数をラップします。Python2.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を表示し、定数を使って操作を行っていることを知ることが最善の方法かもしれません(もっとも 'Pythonic'な方法ではないかもしれません)。

6
Raufio

Tupleは、その値の1つを変更しようとするとエラーを発生させるので、技術的には定数としての資格を持ちます。 1つの値を使ってTupleを宣言したい場合は、次のようにその唯一の値の後にコンマを置きます。

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

この変数の値を確認するには、次のようにします。

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

この値を変更しようとすると、エラーが発生します。

5
tobahhh

「定数」を宣言するPythonicの方法は基本的にモジュールレベルの変数です。

RED = 1
GREEN = 2
BLUE = 3

そして、クラスや関数を書きます。定数はほとんど常に整数であり、Pythonでも不変であるため、変更する可能性はほとんどありません。

もちろん、明示的にRED = 2を設定していない限り。

4
Xavier Ho

Namedtupleを使用してこれを行うためのよりクリーンな方法があります。

from collections import namedtuple


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

使用例

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

これとまったく同じ方法で、定数に名前を付けることができます。

記述子オブジェクトを作成できます。

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

Pythonの辞書は変更可能なので、定数を宣言する良い方法のようには思われません。

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

Pythonでは、定数はすべて大文字で名前を持ち、単語がアンダースコア文字で区切られた単なる変数です。

例えば

DAYS_IN_WEEK = 7

値は変更可能で、変更可能です。しかし、名前の規則によれば、あなたは定数であると言われますが、なぜあなたはそうなるのでしょうか。つまり、それはあなたのプログラムです。

これはPython全体で採用されているアプローチです。同じ理由でprivateキーワードはありません。名前の前にアンダースコアを付けると、名前が非公開になることを意図していることがわかります。プログラマがプライベートキーワードを削除するのと同じように、コードがルールを破る可能性があります。

Pythonはconstキーワードを追加することができました...しかしプログラマーはキーワードを削除し、必要ならば定数を変更することができました、しかしそれはなぜですか?ルールを破りたい場合は、とにかくルールを変更できます。しかし、その名前が意図を明確にしている場合、なぜ規則を破るのを煩わしいのでしょうか。

たぶん、価値の変化を適用することに意味があるような単体テストがあるのでしょうか。現実の世界では週の日数を変更することはできませんが、8日の週に何が起こるかを見るために。このようなケースが1つだけある場合に、言語が例外の作成を停止した場合は、ルールを破る必要があります。アプリケーションではまだ定数であるにもかかわらず、定数としての宣言を停止する必要があります。この1つのテストケースでは、変更された場合に何が起こるのかがわかります。

すべて大文字の名前は、それが定数であることを意図していることを示しています。それが重要です。あなたがとにかく変更する力を持っているのは言語に対する強制的な制約ではありません。

それがPythonの哲学です。

3
innov8

多分pconst libraryがお手伝いします( 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
sim

あなたはStringVarやIntVarなどを使うことができます、あなたの定数は const_val

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

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

単にあなたがすることができます:

STRING_CONSTANT = "hi"
NUMBER_CONSTANT = 89

すべてがずっと簡単になることを願っています

2
user9748068

これを行うための完璧な方法はありません。私が理解しているように、ほとんどのプログラマは識別子を大文字にするだけなので、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がそうなのです。

私のお勧めは、すべての面倒を避け、識別子を大文字にすることだけです。それは本当に適切な定数ではないでしょうが、それからまた何もしないでしょう。

2
John

Pythonでは、定数は存在しません。しかし、変数が定数であることを示すことができます。変数名の先頭にCONST_またはCONSTANT_を追加するか、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.
2
Aryan Beezadhur

collections.namedtupleitertoolsでそれを行うことができます。

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

(この段落は、namedtupleについて言及した here および there に対する回答に対するコメントを意味していましたが、コメントに収まるには長すぎます。)

上記のnamedtupleアプローチは間違いなく革新的です。ただし、完全を期すために、 の公式ドキュメント のNamedTupleセクションの最後には、次のように記載されています。

列挙型定数は名前付きタプルで実装できますが、単純なクラス宣言を使用する方が簡単で効率的です。

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

言い換えれば、公式のドキュメンテーションの種類は実際には読み取り専用の振る舞いを実装するよりも実用的な方法を使用することを好みます。 PythonのZen のもう1つの例になると思います。

単純は複雑よりも優れています。

実用性は純度を打ち負かす。

2
RayLuo

定数が必要でそれらの値を気にしないのであれば、これがトリックです。

空のクラスを定義するだけです。

例えば:

class RED: 
    pass
class BLUE: 
    pass
1
Lym Zoy

私の場合、定数であることを保証したい多くのリテラル番号を含む暗号ライブラリの実装には不変のバイト配列が必要でした。

この答え は動作しますが、バイト配列要素の再割り当てを試みてもエラーにはなりません。

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

より強力で、単純で、そしておそらくもっと 'Pythonic'なアプローチはmemoryviewオブジェクト(<= 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
jxqz

テンキーのない配列で定数をラップし、書き込み専用のフラグを付け、常にインデックス0で呼び出すことができます。

import numpy as np

# declare a constant
CONSTANT = 'hello'

# put constant in numpy and make read only
CONSTANT = np.array([CONSTANT])
CONSTANT.flags.writeable = False
# alternatively: CONSTANT.setflags(write=0)

# call our constant using 0 index    
print 'CONSTANT %s' % CONSTANT[0]

# attempt to modify our constant with try/except
new_value = 'goodbye'
try:
    CONSTANT[0] = new_value
except:
    print "cannot change CONSTANT to '%s' it's value '%s' is immutable" % (
        new_value, CONSTANT[0])

# attempt to modify our constant producing ValueError
CONSTANT[0] = new_value



>>>
CONSTANT hello
cannot change CONSTANT to 'goodbye' it's value 'hello' is immutable
Traceback (most recent call last):
  File "shuffle_test.py", line 15, in <module>
    CONSTANT[0] = new_value
ValueError: assignment destination is read-only

もちろん、これはnumpyの内容だけを保護し、変数 "CONSTANT"自体は保護しません。あなたはまだ行うことができます:

CONSTANT = 'foo'

CONSTANTは変更されますが、スクリプト内で後でCONSTANT[0]が呼び出されたときに、TypeErrorがすぐにスローされます。

しかし...私はあなたがある時点でそれをに変更したかと思います

CONSTANT = [1,2,3]

今、あなたはもうTypeErrorを受けないでしょう。うーん…….

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

1
litepresence

私はpython const用のutil libを書きます: kkconst - pypi str、int、float、datetimeをサポートします

constフィールドインスタンスはその基本型の振る舞いを維持します。

例えば:

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

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

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

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

より詳細な使い方あなたはpypiのURLを読むことができます: pypi または github

1
kaka_ace

次のクラスを使って定数変数をエミュレートすることができます。使用例

# Const
const = Const().add(two=2, three=3)

print 'const.two: ', const.two
print 'const.three: ', const.three

const.add(four=4)

print 'const.four: ', const.four

#const.four = 5 # a error here: four is a constant

const.add(six=6)

print 'const.six: ', const.six

const2 = Const().add(five=5) # creating a new namespace with Const()
print 'const2.five: ', const2.five
#print 'const2.four: ', const2.four # a error here: four does not exist in const2 namespace

const2.add(five=26)

新しい定数名前空間を開始したいときはコンストラクタを呼び出します。 Martelliの const classがそうでない場合、クラスは 保護の下で 予期しないシーケンス型定数の変更から保護されます。

ソースは以下です。

from copy import copy

class Const(object):
"A class to create objects with constant fields."

def __init__(self):
    object.__setattr__(self, '_names', [])


def add(self, **nameVals):
    for name, val in nameVals.iteritems():          
        if hasattr(self, name):
            raise ConstError('A field with a name \'%s\' is already exist in Const class.' % name)

        setattr(self, name, copy(val)) # set up getter

        self._names.append(name)

    return self


def __setattr__(self, name, val):
    if name in self._names:
        raise ConstError('You cannot change a value of a stored constant.')

    object.__setattr__(self, name, val)
0
sergzach

他のすべての答えは正しいです。 Pythonには「定数変数」の概念はありませんが、オーバーロードされた__setattr__メソッドを持つカスタム定義クラスを使用してこの機能をエミュレートできます。しかし、python3.4以降では、 列挙型、または "定数"の定義に適合するEnum に紹介されているので、このプロセスは非常に単純で指定しやすくなります。本当の意味:

from enum import Enum
import math

class Constants(Enum):
    PI = math.pi
    EXP = math.e
    GOLDEN_RATIO = (1 + 5 ** 0.5) / 2

実際の値はvalue属性を使ってアクセスできます。

Constants.PI.value
#  3.141592653589793

この値は変更または再割り当てできません。

Constants.PI = 123
# AttributeError: Cannot reassign members.

詳しくは ドキュメント を参照してください。

0
cs95

これは、すでに利用可能な回答の一部を改善するために作成したイディオムのコレクションです。

定数の使用はPythonicではないことを知っています。これを自宅で行うべきではありません。

ただし、Pythonは非常に動的な言語です。このフォーラムは、定数のように見えて感じる構造の作成がどのように可能であるかを示しています。この回答の主な目的は、言語で表現できるものを調査することです。

厳しすぎないでください:-)。

詳細については、 これらのイディオムに関する伴奏ブログ を書きました。

この投稿では、値への定数参照(不変またはその他)に定数変数を呼び出します。さらに、クライアントコードが値を更新できない可変オブジェクトを参照する場合、変数の値は固定されていると言います。

定数のスペース(SpaceConstants)

このイディオムは、定数変数の名前空間のように見えるものを作成します(a.k.a. SpaceConstants)。モジュールオブジェクトの使用を避けるための Alex Martelli によるコードスニペットの変更です。特に、この変更では、SpaceConstants関数内で、SpaceConstantsが定義され、そのインスタンスが返されます。

クラスファクトリを使用して、 stackoverflow および blogpost のPythonのようなポリシーベースのデザインを実装することを検討しました。

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の修正です。この実装は、私が呼ぶものを利用します 共有クロージャ betweensetattrgetattr関数。可変オブジェクトの値はコピーされ、変数cacheによって参照されます。関数共有クロージャー内で定義します。これは、私が 可変オブジェクトのクロージャー保護コピー と呼ぶものを形成します。

getattrはディープコピーを実行してキャッシュの値を返すため、このイディオムの使用には注意が必要です。この操作は、大きなオブジェクトのパフォーマンスに大きな影響を与える可能性があります!

from copy import deepcopy

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

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

一定のスペース(ConstantSpace)

このイディオムは、定数変数またはConstantSpaceの不変の名前空間です。 stackoverflowclass factory の非常にシンプルなJon Bettsの答えの組み合わせです。

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'"
0
Victor Bazterra

ここでのすべての答えのうち、Pythonで定数変数を作成する最も簡単な方法があります。一次元のタプルを作るだけです。

myconstant_var = (10,)

それでおしまい。変数myconstant_varは変更できません

0
Heapify

Raufioの答えを拡張して、値を返すために repr を追加してください。

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)
    def __repr__(self):
        return ('{0}'.format(self.value))

dt = const(float(0.01))
print dt

そうすれば、オブジェクトはあなたが予想しているよりももう少し振る舞い、 '。value'ではなく直接アクセスすることができます。

これは古くなっていますが、ここに2セントを追加しましょう:-)

class ConstDict(dict):
    def __init__(self, *args, **kwargs):
        super(ConstDict, self).__init__(*args, **kwargs)

    def __setitem__(self, key, value):
        if key in self:
            raise ValueError("Value %s already exists" % (key))
        super(ConstDict, self).__setitem__(key, value)

ValueErrorが壊れる代わりに、そこで起こる更新を防ぐことができます。この利点の1つは、プログラム内で定数を動的に追加できますが、定数が設定されると変更できないことです。また、定数を設定する前に任意の規則やその他のものを追加することもできます(キーを設定する前に、keyやstring、小文字のストリング、大文字のストリングなどにしてください)

しかし、Pythonで定数を設定することの重要性はわかりません。 Cのように最適化は起こり得ないので、それは必要ではないということです、と私は思います。

0
thiruvenkadam