서론
Python 3.11에서는 str, Enum
다중상속 클래스의 문자열 출력 동작이 크게 변경되었습니다.
이 변화는 열거형 클래스의 일관성을 높이기 위한 의도적인 수정이지만,
이미 작성된 코드 호환성에 직접적인 영향을 주므로 주의가 필요합니다.
목차
1. Python 3.10과 3.11의 차이점
1
2
3
4
5
6
7
8
9
10
11
12
# Python 3.10
from enum import Enum
class Color(str, Enum):
RED = 'red'
GREEN = 'green'
BLUE = 'blue'
print(f"{Color.RED}") # 'red'
print(str(Color.RED)) # 'Color.RED'
print("{}".format(Color.RED)) # 'red'
print("%s" % Color.RED) # 'Color.RED'
1
2
3
4
5
6
7
8
9
10
11
12
# Python 3.11
from enum import Enum
class Color(str, Enum):
RED = 'red'
GREEN = 'green'
BLUE = 'blue'
print(f"{Color.RED}") # 'Color.RED' - 변경됨!
print(str(Color.RED)) # 'Color.RED'
print("{}".format(Color.RED)) # 'Color.RED' - 변경됨!
print("%s" % Color.RED) # 'Color.RED'
이 변경은 Python의 Enum 클래스 동작을 더 일관되게 만들기 위한 의도적인 수정입니다.
Python 3.10까지는 str, Enum
다중상속 클래스가
f-string이나 str.format()에서 사용될 때 __format__
메서드가 값을 반환했지만,
str()
함수나 %-형식 문자열에서는 클래스와 멤버 이름을 반환했습니다.
3.11부터는 이 불일치가 해소되어 모든 문자열 변환 상황에서 클래스명과 멤버명을 함께 출력하도록 바뀌었습니다12.
2. 영향받는 코드
이 변경은 다음과 같은 상황에서 문제를 일으킬 수 있습니다:
- f-string에서 Enum 멤버를 직접 사용하는 코드
str.format()
을 사용한 포맷팅- Enum 값이 문자열로 자동 변환되는 API 호출
1
2
3
# 3.10에선 작동하나 3.11에선 실패하는 코드
def make_path(color: Color) -> str:
return f"/colors/{color}" # 3.10: "/colors/red", 3.11: "/colors/Color.RED"
3. 해결책: StrEnum 사용하기
Python 3.11에서는 이 문제를 해결하기 위해 StrEnum
클래스가 새로 추가되었습니다34.
1
2
3
4
5
6
7
8
9
10
# Python 3.11
from enum import StrEnum
class Color(StrEnum):
RED = 'red'
GREEN = 'green'
BLUE = 'blue'
print(f"{Color.RED}") # 'red'
print(str(Color.RED)) # 'red'
StrEnum
은 문자열 포맷팅 시 값을 반환하도록 설계된 새로운 클래스입니다.
기존 Python 3.10의 str, Enum
다중상속이 f-string에서만 값을 반환했던 것과 달리,
StrEnum
은 모든 문자열 변환 상황에서 일관되게 값을 반환합니다12.
4. 하위 호환성 유지 방법
Python 3.10 이하 버전과 3.11 이상을 모두 지원하려면:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
try:
from enum import StrEnum
except ImportError:
from enum import Enum
class StrEnum(str, Enum):
"""Python 3.10 이하에서 StrEnum 에뮬레이션"""
def __str__(self):
return self.value
def __format__(self, format_spec):
return format(self.value, format_spec)
class Color(StrEnum):
RED = 'red'
GREEN = 'green'
BLUE = 'blue'
위 코드는 Python 3.11에서는 내장된 StrEnum
을 사용하고,
이전 버전에서는 str, Enum
다중상속에 __str__
과 __format__
메서드를 직접 구현하여 동일한 동작을 구현합니다5.
5. 테스트 권장사항
여러 Python 버전에서의 동작을 확인하기 위해 다음 테스트를 권장합니다:
1
2
3
4
5
6
def test_enum_string_format():
assert f"{Color.RED}" == "red"
assert str(Color.RED) == "red"
assert "{}".format(Color.RED) == "red"
assert "{:>5}".format(Color.RED) == " red" # 포맷 지정자 테스트
assert f"{Color.RED:>5}" == " red" # f-string 포맷 지정자 테스트
6. 결론
이 변경사항은 Python 3.11로 업그레이드하는 많은 프로젝트에 영향을 줄 수 있으므로,
미리 코드를 점검하고 대응하는 것이 중요합니다.
특히 f-string이나 문자열 포맷팅을 사용하는 코드는 반드시 테스트를 통해 동작을 확인해야 합니다.