Python : 파이썬에서 어떻게 상수를 정의하고 사용할까?

pm1100tm·2021년 1월 27일
5

python

목록 보기
22/23
post-thumbnail

파이썬을 공부하다가 문득 떠오른.. 상수를 어떻게 설정하지? 였는데, 딱히 상수를 어떻게 설정하고 사용하라는 컨벤션 같은 것은 없는 것 같아서 조사를 해보았다.


첫번 째, 대문자를 사용해서 상수 명시하기

프로그램밍 언어에서 관례적으로 이 변수에는 다른 값을 할당하지 마세요 라는 약속을 할 때 대문자와 _ 를 사용하여 변수를 선언한다.

Java의 경우 보통 static final 로 선언한다.

public static final string CONST_NAME = "VAR_NAME"

이렇게 선언하면 해당 상수에 다른 값을 대입하려고 하면 에러를 발생시킨다.
하지만 Python 은 동적 언어이기 때문에 별도로 타입을 지정하지도 않으며, final 이라는 키워드도 없다. 따라서, 명시적으로 대문자와 _ 를 사용해서 상수를 선언하는 방법이 있다.


두번 째, 상수를 모듈단위로 선언하는 방법이 있다.

아래의 코드는 위키에서 가져온 샘플이다. 상수를 정의하는 .py 모듈을 만들고 그 곳에 상수를 정의한다. 그리고 해당 모듈을 다른 .py 모듈에서 import 하여 사용하는 방법이다.

"""constant1.py"""
PI = 3.14
GRAVITY = 9.8


"""Ex06_constant1.py"""
import constant1

print(constant1.PI)
print(constant1.GRAVITY)
constant1.PI = 3.141592 # 변경된다. 상수가 아니다.
print(constant1.PI)

여러 곳에서 공통적으로 사용하는 상수를 한 곳에 모아 놓을 수 있는 장점이 있다.
하지만 역시 선언한 변수에 다른 값을 넣으면 변경된다. 온전한 상수가 아니다.


세번 째, Type.hint 사용해서 좀 더 명확하게 정의하기

Python 3.8 부터는 typing.Final 를 사용해서, 변수의 재할당을 방지하도록 하는 변수 주석이 있다고 한다. 그러나 실제로 재할당을 막지는 않는다.

from typing import Final

a: Final = 1

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

네번 째, 클래스를 사용하여 정의한다.

값이 변경된다는 단점을 보완하는 방법이다. 찾아보니 위키 교제에 나와 있었다.

"""constant2.py"""
class _constant2:
    def __setattr__(self, name, value):
        if name in self.__dict__:
            raise Exception('변수에 값을 할당할 수 없습니다.')
        self.__dict__[name] = value

    def __delattr__(self, name):
        if name in self.__dict__:
            raise Exception('변수를 삭제할 수 없습니다.')

import sys
sys.modules[__name__] = _constant2()


"""OOO.py"""
import constant2

constant2.PI = 3.14
print(constant2.PI)

constant2.PI = 3.141592
print(constant2.PI)

상수를 저장할 class 를 만들고 파이썬에서 제공하는 setattr 을 변수를 설정한다.

  • 해당 모듈을 실행시켜서 초기에 변수에 값을 할당한다.
  • 클래스가 이미 같은 변수(키)를 가지고 있다면, 에러를 발생시켜서 새로운 값을 할당하지 못하게 막는다.
  • 같은 맥락으로 값을 지우려고 할 때, 에러를 발생시킨다.

뭔가 동적으로 상수를 관리하는 느낌이다.


다섯번 째, 클래스와 데코레이터를 사용하여 상수 정의하기.

코드를 보자.

# Decorator
def constant(func):
    def func_set(self, value):
        raise TypeError

    def func_get(self):
        return func()
    return property(func_get, func_set)

# const class
class _Const(object):
    @constant
    def PAGE_INDEX():
        return "/index"

    @constant
    def PAGE_MEMBER_LOGIN():
        return "/member/login"

    @constant
    def PAGE_MEMBER_LOGOUT():
        return "/member/logout"

CONST = _Const()

print(CONST.PAGE_INDEX) # /index
print(CONST.PAGE_MEMBER_LOGIN) # /member/login
print(CONST.PAGE_MEMBER_LOGOUT) # /member/logout

CONST.PAGE_MEMBER_LOGIN = "another_page" # TypeError
  • const 라는 데코레이터를 만들었다. 다시보니 데코레이터는 클로져의 형태였다.
  • _Const 라는 클래스를 선언하여, 상수 값을 리턴하는 메서드를 정의한다.
  • 이제 _Const() 객체를 만들고, 해당 객체가 가지고 있는 메서드를 호출하게 되면, 상수 값이 나온다.
  • 값을 재할당하려고 하면, 데코레이터에 의해서 TypeError 가 나오는 것을 확인할 수 있다.

여기까지 조사하니 좀 지치기 시작했다.. 왜냐면 다른 방법들이 너무 많았기 때문인데.. 조사한 바에 따르면 아래의 방법들이 더 있었다.

  • __slots__ 이용하기
  • property 이용하기
  • metaclass 사용하기
  • named tuple 사용하기
  • SpaceConstants
  • SpaceFrozenValues
  • ConstantSpace
  • FrozenSpace

음.. Final 만 사용해도 충분하지 않을까..
프로젝트가 커지면 커질수록 관리하는 방법이 중요하다고 했는데,, 일단은 여기까지 하도록 하자. 파고들기엔 아직 배워야 할 파이썬 기술들이 많기 때문에 !
나중을 기약하도록 한다. 개인적으로는 JS ES6 처럼 const 가 나와줬으면 한다.

profile
개발을 취미로 할 수 있는 그 때 까지

2개의 댓글

comment-user-thumbnail
2021년 3월 8일

상세하고 자세한 글들이 많네요! 책쓰시나봐요! 너무 잘 참고하고 갑니다!^^

1개의 답글