development

파이썬에서 상수를 어떻게 생성합니까?

big-blog 2020. 9. 28. 09:29
반응형

파이썬에서 상수를 어떻게 생성합니까?


파이썬에서 상수를 선언하는 방법이 있습니까? Java에서는 다음과 같은 방식으로 상수 값을 만들 수 있습니다.

public static final String CONST_NAME = "Name";

Python에서 위의 Java 상수 선언에 해당하는 것은 무엇입니까?


아니 없어. Python에서는 변수 나 값을 상수로 선언 할 수 없습니다. 변경하지 마십시오.

클래스에있는 경우 해당하는 것은 다음과 같습니다.

class Foo(object):
    CONST_NAME = "Name"

그렇지 않다면 그것은 단지

CONST_NAME = "Name"

하지만 Alex Martelli가 작성한 Python의 Constants 코드 스 니펫을보고 싶을 수도 있습니다 .


더 없어 const다른 언어로 키워드, 그러나 재산권 만들 수 는 "게터 기능"이 데이터를 읽을 수있는,하지만 아니오 "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는 두 가지 기능을 정의합니다 (fset, fget-이름은 내가 선택합니다).
  2. 그런 다음 내장 property함수를 사용하여 "set"또는 "get"할 수있는 객체를 생성합니다.
  3. 참고 모자는 property함수의 처음 두 매개 변수의 이름은 fset하고 fget.
  4. 자체 getter 및 setter에 대해 이러한 이름을 선택했다는 사실을 사용하고 해당 범위의 모든 로컬 정의에 적용된 ** (이중 별표)를 사용하여 키워드 사전을 만들어 매개 변수를 property함수에 전달 합니다.

파이썬에서 어떤 것을 강제하는 대신에 사람들은 이름 지정 규칙을 __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

최근에 의미있는 오류 메시지를 자동으로 발생시키고 __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물건 에 많은 것을 붙일 수 있습니다 . 상류층이 있으면 클래스 이름도 약간 엉뚱 해 보이지만 전반적으로 꽤 간결하다고 생각합니다.


파이썬에는 상수가 없습니다.

아마도 가장 쉬운 대안은 함수를 정의하는 것입니다.

def MY_CONSTANT():
    return 42

MY_CONSTANT() 이제 상수의 모든 기능과 일부 성가신 중괄호가 있습니다.


두 개의 상위 답변 (대문자 이름으로 변수를 사용하거나 속성을 사용하여 값을 읽기 전용으로 설정) 외에도 명명 된 상수 를 구현하기 위해 메타 클래스를 사용할 수 있음을 언급하고 싶습니다 . GitHub 에서 메타 클래스를 사용하는 매우 간단한 솔루션을 제공합니다. 이는 값이 유형 / 이름에 대해 더 많은 정보를 얻고 자 할 때 도움이 될 수 있습니다.

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

이것은 약간 더 고급 Python이지만 여전히 사용하기 쉽고 편리합니다. (모듈에는 읽기 전용 상수를 포함하여 더 많은 기능이 있습니다. README를 참조하십시오.)

다양한 리포지토리에 유사한 솔루션이 떠돌고 있지만, 내가 아는 한 그들은 상수에서 기대할 수있는 기본 기능 (예 : 상수 또는 임의 유형) 중 하나가 없거나 난해한 기능이 추가되었습니다. 덜 일반적으로 적용되도록 만듭니다. 하지만 YMMV, 피드백 감사합니다. :-)


편집 : Python 3 용 샘플 코드 추가

참고 : 이 다른 답변 은 다음과 유사한 훨씬 더 완전한 구현을 제공하는 것 같습니다 (더 많은 기능 포함).

먼저 메타 클래스를 만듭니다 .

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

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

이렇게하면 정적 속성이 변경되지 않습니다. 그런 다음 해당 메타 클래스를 사용하는 다른 클래스를 만듭니다.

class Const(object):
    __metaclass__ = MetaConst

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

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

또는 Python 3을 사용하는 경우 :

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

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

이렇게하면 인스턴스 소품이 변경되는 것을 방지 할 수 있습니다. 그것을 사용하려면 상속 :

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

이제 직접 또는 인스턴스를 통해 액세스되는 props는 일정해야합니다.

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

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

다음 은 위의 실제 사례입니다. 다음 은 Python 3 또 다른 예입니다.


속성은 상수를 만드는 한 가지 방법입니다. getter 속성을 선언하고 setter를 무시하면됩니다. 예를 들면 :

class MyFinalProperty(object):

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

당신은 한 번 봐 가질 수 있습니다 내가 쓴 기사 파이썬 속성을 사용하여 더 많은 방법을 찾을 수 있습니다.


다음은 읽기 전용 (상수) 속성으로 인스턴스를 생성하는 "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 (__name__ == "__main__"):
    # Usage example.
    Nums = Constants(ONE=1, PI=3.14159, DefaultWidth=100.0)
    print 10 + Nums.PI
    print '----- Following line is deliberate ValueError -----'
    Nums.PI = 22

@MikeGraham의 FrozenDict 덕분에 내가 시작점으로 사용했습니다. 변경되었으므로 Nums['ONE']사용 구문 대신 Nums.ONE.

그리고 __ setattr __을 재정의하는 아이디어에 대한 @Raufio의 답변 덕분에.

또는 더 많은 기능을 구현하려면 GitHub에서 @Hans_meinenamed_constants를 참조하십시오.


해결 방법으로 namedtuple을 사용하여 Java의 정적 최종 변수 (Java "상수")와 동일한 방식으로 작동하는 상수를 효과적으로 생성 할 수 있습니다. 해결 방법이 갈수록 우아합니다. (더 우아한 접근 방식은 단순히 Python 언어를 개선하는 것입니다 .--- 어떤 종류의 언어를 재정의 할 수 math.pi있습니까?-하지만 저는 우회합니다.)

(이 글을 쓰는 동안이 질문에 대한 또 다른 대답 인 namedtuple을 알게되었지만, 여기서 계속 진행하겠습니다. 왜냐하면 명명 된 튜플을 만들 필요가 없기 때문에 Java에서 기대했던 것과 더 유사한 구문을 보여주기 때문입니다. namedtuple로 입력 하면 강제로 수행됩니다.)

예제를 따르면 Java에서 일부 클래스 내부 에 상수를 정의해야한다는 것을 기억할 것입니다 . 클래스 이름을 언급하지 않았으므로 이름을 Foo. 다음은 Java 클래스입니다.

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

여기에 상응하는 Python이 있습니다.

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

내가 여기에 추가 할 중요한 점은 별도의 필요가 없다는 것입니다 Foo, 우리는 우리의 namedtuple 이름 있도록 (모순 어법 같은 그 소리에도 불구하고,에 "익명의 이름 튜플"될 좋은를) 유형을 _Foo그 정도 희망은하지 않습니다 모듈 가져 오기로 이스케이프하십시오.

여기서 두 번째 요점은 우리 가 이름 튜플 의 인스턴스즉시 생성하여 호출한다는 것입니다 Foo. 별도의 단계에서이 작업을 수행 할 필요가 없습니다 (원하지 않는 한). 이제 Java에서 수행 할 수있는 작업을 수행 할 수 있습니다.

>>> Foo.CONST_NAME
'Name'

그러나 할당 할 수 없습니다.

>>> Foo.CONST_NAME = 'bar'
AttributeError: can't set attribute

Acknowledgement : 내가 namedtuple 접근 방식을 발명했다고 생각했지만 다른 누군가가 비슷한 (덜 간결하지만) 대답을 한 것을 봅니다. 그리고 나는 또한 파이썬에서 "명명 된 튜플"이 무엇 인지 알아 차 렸습니다 . , 이는 sys.version_info이제 명명 된 튜플 이라는 점을 지적합니다 . 따라서 아마도 Python 표준 라이브러리는 이미이 아이디어를 훨씬 일찍 생각해 냈습니다.

안타깝게도 (여전히 Python 임) 전체 Foo할당을 모두 지울 수 있습니다 .

>>> Foo = 'bar'

(페이스 팜)

그러나 적어도 우리는 Foo.CONST_NAME가치가 변경되는 것을 막고 있으며 , 그것이없는 것보다 낫습니다. 행운을 빕니다.


__setattr__기본 개체 클래스 메서드 를 재정의하고 상수를 래핑하는 클래스를 만들고 python 2.7을 사용하고 있습니다.

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

문자열을 감싸려면 :

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

매우 간단하지만 상수가 아닌 객체 (constObj.value를 사용하지 않음)와 동일하게 상수를 사용하려면 좀 더 집중적입니다. 이로 인해 문제가 발생할 수 있으므로 .value상수를 사용하여 작업을 수행하고 있음을 표시하고 아는 것이 가장 좋습니다 (가장 '파이썬'방식은 아닐 수 있음).


튜플은 값 중 하나를 변경하려고하면 튜플이 오류를 발생 시키므로 기술적으로 상수로 한정됩니다. 하나의 값으로 튜플을 선언하려면 다음과 같이 유일한 값 뒤에 쉼표를 넣으십시오.

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

이 변수의 값을 확인하려면 다음과 유사한 것을 사용하십시오.

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

이 값을 변경하려고하면 오류가 발생합니다.


불행히도 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 __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로 테스트되었습니다 ( '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

"상수"를 선언하는 파이썬 방식은 기본적으로 모듈 수준 변수입니다.

RED = 1
GREEN = 2
BLUE = 3

그런 다음 클래스 또는 함수를 작성하십시오. 상수는 거의 항상 정수이고 파이썬에서도 불변이므로 변경할 가능성이 거의 없습니다.

물론 명시 적으로 RED = 2.


Python 사전은 변경 가능하므로 상수를 선언하는 좋은 방법이 아닌 것 같습니다.

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

파이썬에서 상수는 밑줄 문자로 구분 된 단어와 함께 모두 대문자로 된 이름을 가진 변수입니다.

예 :

DAYS_IN_WEEK = 7

값은 변경할 수 있으므로 변경할 수 있습니다. 하지만 이름에 대한 규칙이 당신에게 상수라고 말해 주는데 왜 당신은 그럴까요? 결국 그것은 당신의 프로그램입니다!

이것은 파이썬 전체에서 취해진 접근 방식입니다. private같은 이유로 키워드 가 없습니다 . 이름 앞에 밑줄을 붙이면 개인용임을 알 수 있습니다. 프로그래머가 어쨌든 private 키워드를 제거 할 수있는 것처럼 코드는 규칙을 깨뜨릴 수 있습니다.

파이썬은 const키워드를 추가 할 수 있었지만 프로그래머는 키워드를 제거한 다음 원하는 경우 상수를 변경할 수 있지만 왜 그렇게할까요? 규칙을 어기려면 어쨌든 규칙을 변경할 수 있습니다. 하지만 이름이 의도를 분명히한다면 왜 규칙을 어기려고할까요?

값에 변경 사항을 적용하는 것이 합당한 단위 테스트가 있습니까? 현실 세계에서는 요일 수를 변경할 수 없지만 주 8 일 동안 어떤 일이 발생하는지 확인합니다. 언어가 예외를 만드는 것을 막았다면 규칙을 어길 필요가있는 경우에만 예외를 만들면 ... 그런 다음 응용 프로그램에서 여전히 상수 인 경우에도 상수로 선언하는 것을 중지해야합니다. 변경 될 경우 어떻게되는지 확인하는이 하나의 테스트 케이스입니다.

모두 대문자 이름은 상수가되도록 의도되었음을 알려줍니다. 그것이 중요한 것입니다. 코드에 대한 제약을 강요하는 언어가 아니라 어쨌든 변경할 수있는 힘이 있습니다.

이것이 파이썬의 철학입니다.


namedtuple을 사용하여이를 수행하는 더 깨끗한 방법이 있습니다.

from collections import namedtuple


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

사용 예

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

이 방법을 사용하면 상수에 네임 스페이스를 지정할 수 있습니다.


아마도 pconst 라이브러리가 도움이 될 것입니다 ( github ).

$ pip install pconst

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

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


설명자 객체를 만들 수 있습니다.

class 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

상수를 원하고 그 값을 신경 쓰지 않는 경우 트릭이 있습니다.

빈 클래스를 정의하십시오.

예 :

class RED: 
    pass
class BLUE: 
    pass

StringVar 또는 IntVar 등을 사용할 수 있으며 상수는 const_val입니다.

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

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

간단히 다음을 수행 할 수 있습니다.

STRING_CONSTANT = "hi"
NUMBER_CONSTANT = 89

모든 것이 훨씬 간단 해지기를 바랍니다.


이를위한 완벽한 방법은 없습니다. 내가 이해했듯이 대부분의 프로그래머는 식별자를 대문자로 표시하므로 PI = 3.142는 상수로 쉽게 이해할 수 있습니다.

반면에 실제로 상수처럼 작동하는 무언가를 원한다면 찾을 수 있을지 모르겠습니다. 무엇을하든 항상 "상수"를 편집하는 방법이 있으므로 실제로는 상수가 아닙니다. 다음은 매우 간단하고 더러운 예입니다.

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

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

define("PI",3.142)

print(constant("PI"))

이것은 PHP 스타일의 상수를 만드는 것처럼 보입니다.

실제로 누군가가 값을 변경하는 데 필요한 것은 다음과 같습니다.

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

이것은 여기에서 찾을 수있는 다른 모든 솔루션에 대해 동일합니다. 심지어 클래스를 만들고 set attribute 메서드를 재정의하는 영리한 솔루션도 마찬가지입니다. 항상 해결 방법이있을 것입니다. 그것이 바로 파이썬의 방식입니다.

내 권장 사항은 모든 번거 로움을 피하고 식별자를 대문자로 사용하는 것입니다. 실제로 적절한 상수는 아니지만 다시는 아무것도 할 수 없습니다.


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

(이 단락은 그 답변에 댓글로 의미되었다 여기거기에 언급, namedtuple하지만 그래서 여기 간다 댓글에 맞는로 너무 오래지고 있습니다.)

위에서 언급 한 namedtuple 접근 방식은 확실히 혁신적입니다. 그러나 완전성을 위해 공식 문서 의 NamedTuple 섹션 끝에 다음과 같이 표시됩니다 .

열거 형 상수는 명명 된 튜플을 사용하여 구현할 수 있지만 간단한 클래스 선언을 사용하는 것이 더 간단하고 효율적입니다.

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

즉, 공식 문서는 실제로 읽기 전용 동작을 구현하는 것보다 실용적인 방법을 사용하는 것을 선호합니다. Zen of 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.

제 경우에는 일정하게 유지하고 싶은 많은 리터럴 숫자를 포함하는 암호화 라이브러리를 구현하기 위해 변경 불가능한 바이트 배열이 필요했습니다.

이 답변 은 작동하지만 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')

보다 강력하고 간단하며 아마도 더 '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

python const에 대한 util lib를 작성합니다. kkconst-pypi support 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


상수를 numpy 배열로 래핑하고 쓰기 전용 플래그를 지정하고 항상 인덱스 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

물론 이것은 변수 "CONSTANT"자체가 아니라 numpy의 내용만을 보호합니다. 당신은 여전히 ​​할 수 있습니다 :

CONSTANT = 'foo'

CONSTANT변경 것, 그러나 그 신속 처음으로가 형식 오류 던질 것입니다 CONSTANT[0]나중에 스크립트에서 호출됩니다.

비록 ... 당신이 어떤 시점에서 그것을 변경했다면

CONSTANT = [1,2,3]

이제 더 이상 TypeError가 발생하지 않습니다. 흠 ....

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


Raufio의 대답을 확장 __repr__하여 값을 반환하려면 a 추가하십시오 .

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'대신 직접 액세스 할 수 있습니다.

참고 URL : https://stackoverflow.com/questions/2682745/how-do-i-create-a-constant-in-python

반응형