Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 661 – 센티널 값

Author:
Tal Einat <tal at python.org>, Jelle Zijlstra <jelle.zijlstra at gmail.com>
Discussions-To:
Discourse thread
Status:
Final
Type:
Standards Track
Created:
06-Jun-2021
Python-Version:
3.15
Post-History:
20-May-2021, 06-Jun-2021
Resolution:
23-Apr-2026

Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain or CC0-1.0, whichever is more permissive 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

Important

This PEP is a historical document. The up-to-date, canonical documentation can now be found at sentinel.

×

See PEP 1 for how to propose changes.

초록

흔히 “센티널 값”이라고 부르는 고유한 자리 표시자 값은 프로그래밍에서 널리 쓰입니다. 센티널 값은 다음과 같이 여러 용도로 쓰입니다:

  • 값이 주어지지 않았을 때를 나타내는 함수 인자의 기본값:
    def foo(value=None):
        ...
    
  • 무언가를 찾을 수 없거나 사용할 수 없을 때 함수가 돌려주는 반환값:
    >>> "abc".find("d")
    -1
    
  • 관계형 데이터베이스의 NULL이나 스프레드시트의 “N/A”(“not available”, 사용할 수 없음)와 같은 누락된 데이터

Python에는 None이라는 특별한 값이 있으며, 대부분의 경우 이 값을 그러한 센티널 값으로 사용하도록 의도되었습니다. 그러나 때로는 다른 센티널 값이 필요합니다. 보통 해당 문맥에서 None이 유효한 값이어서 센티널 값이 None과 구별되어야 하는 경우입니다. 이러한 경우는 그런 센티널을 구현하는 여러 관용구가 수년에 걸쳐 생겨날 만큼 흔하지만, 표준화가 뚜렷이 필요할 만큼 흔하지는 않았습니다. 그러나 표준 라이브러리에 있는 일부 구현을 포함하여 흔히 쓰이는 구현에는 몇 가지 중대한 단점이 있습니다.

이 PEP는 센티널 값을 정의하는 내장 클래스를 추가하여 표준 라이브러리에서 사용하고 모든 Python 코드에 공개적으로 제공할 것을 제안합니다. 센티널은 Python에서는 sentinel() 내장 클래스로, C에서는 PySentinel_New() C API 함수로 정의할 수 있습니다.

참고: 표준 라이브러리에 있는 기존 센티널을 모두 이 방식으로 구현하도록 바꿀 필요는 없다고 판단하며, 바꿀지 여부는 유지 관리자의 재량에 맡깁니다.

동기

2021년 5월, python-dev 메일링 리스트 [1]에서 traceback.print_exception의 센티널 값을 더 잘 구현하는 방법에 관한 질문이 제기되었습니다. 기존 구현은 다음과 같은 흔한 관용구를 사용했습니다:

_sentinel = object()

그러나 이 객체의 repr은 정보를 거의 주지 못하면서 지나치게 장황하여, 함수 시그니처가 지나치게 길고 읽기 어려워집니다:

>>> help(traceback.print_exception)
Help on function print_exception in module traceback:

print_exception(exc, /, value=<object object at
0x000002825DF09650>, tb=<object object at 0x000002825DF09650>,
limit=None, file=None, chain=True)

또한 논의 과정에서 기존의 많은 센티널이 지닌 다른 두 가지 단점이 제기되었습니다:

  1. 일부 센티널은 고유한 타입이 없어서, 그러한 센티널을 기본값으로 쓰는 함수에 명확한 타입 시그니처를 정의할 수 없습니다.
  2. 복사하면 별도의 인스턴스가 생성되어 is를 사용한 비교가 실패하므로, 복사된 뒤에는 예상과 다르게 동작합니다. 흔히 쓰이는 일부 센티널 관용구는 피클링했다가 언피클링한 뒤에도 비슷한 문제가 있습니다.

이어진 논의에서 Victor Stinner가 Python 표준 라이브러리에서 현재 사용 중인 센티널 값의 목록을 제공했습니다 [2]. 이 목록을 통해 센티널이 꽤 흔히 필요하다는 점, 표준 라이브러리 안에서조차 다양한 구현 방식이 쓰인다는 점, 그리고 그중 다수에 위에서 언급한 세 가지 단점 중 적어도 하나가 있다는 점이 드러났습니다.

이 논의에서는 표준 구현 방식이 필요하거나 바람직한지, 언급된 단점이 중대한지, 어떤 종류의 구현이 좋을지에 대해 명확한 합의에 이르지 못했습니다. 이 PEP의 저자는 bugs.python.org에 개선 방안을 제안하는 이슈(현재는 GitHub 이슈 [3])를 만들었지만, 그 이슈는 몇몇 사례에서 문제가 되는 한 가지 측면에만 초점을 맞추었고 지지를 얻지 못했습니다.

커뮤니티의 의견을 더 명확히 파악하기 위해 discuss.python.org에 설문 [4]이 만들어졌습니다. 거의 2주에 걸쳐 상당한 추가 논의가 이루어지고 39표가 모였지만, 설문 결과는 결정적이지 않았습니다. 40%가 “현 상태로 충분함 / 이 부분에서 일관성은 필요 없음”에 투표했지만, 대부분의 투표자는 하나 이상의 표준화된 해법에 투표했습니다. 구체적으로 투표자의 37%가 “표준 라이브러리에서 공개적으로도 제공되는 새로운 전용 센티널 팩토리 / 클래스 / 메타클래스를 일관되게 사용”을 선택했습니다.

이처럼 의견이 엇갈렸기 때문에, 이 주제에 관한 결정을 돕고자 이 PEP를 작성했습니다.

저자는 이 PEP를 작성하면서 여러 방안과 구현을 거듭 다듬고 논의를 이어 가는 과정에서, 표준 라이브러리 자체와 그 밖의 곳 모두에서 쓸 수 있도록 표준 라이브러리에 간단하고 좋은 구현을 두는 것이 가치 있다는 의견을 갖게 되었습니다.

근거

선택된 구현의 기준은 다음과 같습니다:

  1. 센티널 객체는 센티널 객체에 기대되는 대로 동작해야 합니다. 즉, is 연산자로 비교할 때 항상 자기 자신과는 동일한 것으로 간주되어야 하지만, 다른 어떤 객체와도 동일한 것으로 간주되어서는 안 됩니다.
  2. 센티널 객체는 간단하고 직관적인 한 줄 코드로 생성할 수 있어야 합니다.
  3. 서로 다른 센티널 값을 필요한 만큼 간단하게 정의할 수 있어야 합니다.
  4. 센티널 객체는 명확하고 짧은 repr을 가져야 합니다.
  5. 센티널에 명확한 타입 시그니처를 사용할 수 있어야 합니다.
  6. 센티널 객체는 복사한 후에도 올바르게 동작해야 하며, 센티널은 피클링 및 언피클링될 때 예측 가능하게 동작해야 합니다.
  7. 이러한 센티널은 CPython 3.x와 PyPy3에서 동작해야 하며, 이상적으로는 다른 Python 구현에서도 동작해야 합니다.
  8. 구현에서, 특히 사용에서 가능한 한 단순하고 직관적이어야 합니다. Python을 배울 때 익혀야 할 특별한 것이 하나 더 늘어나지 않도록 하십시오. 필요할 때 쉽게 찾아 사용할 수 있어야 하며, 코드를 읽을 때 보통은 문서를 찾아볼 필요를 느끼지 않을 만큼 명확해야 합니다.

Python 표준 라이브러리 [2]에서 매우 많이 사용되므로 표준 라이브러리에서 사용할 수 있는 구현이 있으면 유용할 것입니다. 표준 라이브러리는 다른 곳에서 제공되는 센티널 객체 구현(예: sentinels [5] 또는 sentinel [6] PyPI 패키지)을 사용할 수 없기 때문입니다.

기존 관용구와 구현을 조사하고 여러 가지 가능한 구현을 검토한 후, API와 구현을 작게 유지하면서 이러한 기준을 충족하도록 아래의 설계를 선택했습니다(Reference Implementation을 참조하십시오).

흔한 사용 사례를 지원하기 위해 두 가지 사용자 정의 지점을 추가합니다:

  • repr 인자를 사용하여 센티널의 repr()을 사용자 정의할 수 있습니다. 표준 라이브러리의 기존 센티널 중 다수는 <missing>과 같은 사용자 정의 repr을 사용하며, 이 인자를 사용하면 이러한 동작을 유지하면서 이 센티널들을 새 API로 전환할 수 있습니다.
  • 센티널의 __module__ 속성은 쓰기가 가능하므로, exec()를 통해 생성된 센티널처럼 흔치 않은 경우에 사용자가 피클링에 사용되는 모듈을 직접 지정할 수 있습니다. 이는 클래스, 함수, TypeVar 객체, 그리고 (Python 3.15부터) 타입 별칭 등 __module__ 속성을 가진 다른 내장 타입의 동작과 일치합니다.

명세

sentinel이라는 이름의 새로운 내장 호출 가능 객체가 추가됩니다.

>>> MISSING = sentinel('MISSING')
>>> MISSING
MISSING

sentinel()은 str이어야 하는 필수 위치 전용 인자 name 하나와 선택적 키워드 전용 인자 repr을 받습니다. name으로 문자열이 아닌 값을 전달하면 TypeError가 발생합니다. 이름은 센티널의 이름으로 사용됩니다. repr 인자가 주어지면 그 값은 센티널 객체의 str()과 repr()에 사용됩니다. 주어지지 않으면 대신 이름이 사용됩니다.

센티널 객체에는 두 개의 공개 속성이 있습니다:

  • __name__은 센티널의 이름입니다.
  • __module__은 sentinel()이 호출된 모듈의 이름입니다. 이 속성은 쓰기가 가능합니다.

sentinel은 서브클래싱할 수 없습니다.

sentinel(name)을 호출할 때마다 새로운 센티널 객체가 반환됩니다. 센티널이 두 곳 이상에서 필요한 경우, 흔히 쓰이는 MISSING = object() 관용구에서와 마찬가지로 센티널을 변수에 할당하고 그 동일한 객체를 명시적으로 재사용해야 합니다:

MISSING = sentinel('MISSING')

def read_value(default=MISSING):
    ...

값이 이러한 센티널인지 확인할 때는 None에 권장되는 것과 마찬가지로 is 연산자를 사용해야 합니다. ==를 사용한 동등성 검사도 예상대로 동작하며, 객체를 자기 자신과 비교할 때만 True를 반환합니다. 일반적으로 if value:나 if not value: 같은 불리언 검사보다는 if value is MISSING: 같은 동일성 검사를 사용해야 합니다.

센티널 객체는 “참 같은(truthy)” 값입니다. 즉, 불리언으로 평가하면 True가 됩니다. 이는 임의의 클래스에 대한 기본 동작과 같으며, Ellipsis의 불리언 값과도 같습니다. 이 점은 “거짓 같은(falsy)” 값인 None과 다릅니다.

copy.copy()나 copy.deepcopy()를 사용하는 등의 방법으로 센티널 객체의 복사본을 만들면 같은 객체가 반환됩니다.

자신을 정의한 모듈에서 이름으로 임포트할 수 있는 센티널은 이름 있는 싱글턴을 위한 표준 pickle 메커니즘을 사용하므로, 피클링했다가 언피클링해도 동일성이 유지됩니다. sentinel()이 센티널을 생성할 때, 호출한 모듈을 센티널의 __module__ 속성으로 기록합니다. 피클링은 센티널을 모듈과 이름으로 기록합니다. 그런 다음 언피클링은 해당 모듈을 임포트하고 이름으로 센티널을 가져오므로, 다음과 같은 왕복 과정에서 동일성이 유지됩니다:

MISSING = sentinel('MISSING')
assert pickle.loads(pickle.dumps(MISSING)) is MISSING

지역 스코프에서 생성되어 이름이 일치하는 모듈 전역 변수나 클래스 속성에 할당되지 않은 센티널처럼, 모듈과 이름으로 임포트할 수 없는 센티널은 피클링할 수 없습니다.

센티널 객체의 repr은 sentinel()에 전달된 name입니다. 암묵적인 모듈 한정은 추가되지 않습니다. 정규화된 repr이 필요하다면 정규화된 이름을 명시적으로 전달해야 합니다:

>>> MyClass_NotGiven = sentinel('MyClass.NotGiven')
>>> MyClass_NotGiven
MyClass.NotGiven

센티널 객체에 대해서는 순서 비교가 정의되지 않습니다. 센티널은 약한 참조(weakref)를 지원하지 않습니다.

타이핑

타입이 지정된 Python 코드에서 센티널을 명확하고 간단하게 사용할 수 있도록, 타입 시스템에 센티널 객체를 위한 특별한 경우를 추가할 것을 제안합니다.

센티널 객체는 타입 표현식에서 사용할 수 있으며, 자기 자신을 나타냅니다. 이는 기존 타입 시스템에서 None을 다루는 방식과 비슷합니다. 예를 들면 다음과 같습니다:

MISSING = sentinel('MISSING')

def foo(value: int | MISSING = MISSING) -> int:
    ...

좀 더 형식적으로 말하면, 타입 검사기는 NAME = sentinel('NAME') 형태의 센티널 생성을 새로운 센티널 객체의 생성으로 인식해야 합니다. sentinel()에 전달된 이름이 객체가 대입되는 이름과 일치하지 않으면 타입 검사기는 오류를 보고해야 합니다.

이 문법으로 정의된 센티널은 타입 표현식에서 사용할 수 있습니다. 이러한 센티널은 센티널 객체 자신만을 유일한 멤버로 가지는 완전 정적 타입을 나타냅니다.

타입 검사기는 is 및 is not 연산자를 사용하여 센티널이 포함된 유니온 타입을 좁히는 기능을 지원해야 합니다:

from typing import assert_type

MISSING = sentinel('MISSING')

def foo(value: int | MISSING) -> None:
    if value is MISSING:
        assert_type(value, MISSING)
    else:
        assert_type(value, int)

타입 표현식에서의 사용을 지원하기 위해, 센티널 객체의 런타임 구현은 typing.Union 객체를 반환하는 __or__ 및 __ror__ 메서드를 가져야 합니다.

타이핑 위원회(Typing Council)는 제안의 이 부분을 지지합니다.

C API

센티널은 C 확장에서도 유용할 수 있습니다. 두 가지 새로운 C API 함수를 제안합니다:

  • PyObject *PySentinel_New(const char *name, const char *module_name, const char *repr)는 새 센티널 객체를 생성합니다. repr은 NULL일 수 있으며, 이 경우 센티널의 repr은 그 이름과 같습니다.
  • bool PySentinel_Check(PyObject *obj)는 객체가 센티널인지 검사합니다.

C 코드는 == 연산자를 사용하여 객체가 특정 센티널인지 검사할 수 있습니다.

하위 호환성

새 내장 객체를 추가하면, 현재 맨 이름 sentinel이 NameError를 일으키는 것에 의존하는 코드는 대신 새 내장 객체를 보게 됩니다. 이는 새 내장 객체를 추가할 때 일반적으로 고려하는 호환성 문제입니다. sentinel이라는 이름의 기존 지역 이름, 전역 이름, 임포트된 이름은 영향을 받지 않습니다. 이미 sentinel이라는 이름을 사용하는 코드는 새 내장 객체를 사용하도록 수정해야 하며, 내장 이름과의 충돌을 경고하는 린터로부터 새로운 린터 오류를 받을 수 있습니다.

교육 방법

새 내장 객체와 기능에 대한 일반적인 형태의 문서, 즉 독스트링, 라이브러리 문서, “What’s New”의 한 절이면 충분합니다.

보안 영향

이 제안은 보안에 영향을 주지 않습니다.

참조 구현

참조 구현은 CPython 풀 리퀘스트 [10]로 제공됩니다. 이전 참조 구현은 전용 GitHub 저장소 [7]에 있습니다. 의도한 동작의 개략은 다음과 같습니다:

class sentinel:
    """Unique sentinel values."""

    __slots__ = ("__name__", "__module__", "_repr")

    def __init_subclass__(cls):
        raise TypeError("type 'sentinel' is not an acceptable base type")

    def __init__(self, name, /, repr=None):
        if not isinstance(name, str):
            raise TypeError("sentinel name must be a string")
        self.__name__ = name
        self.__module__ = sys._getframemodulename(1)
        self._repr = repr if repr is not None else name

    def __repr__(self):
        return self._repr

    def __reduce__(self):
        return self.__name__

    def __copy__(self):
        return self

    def __deepcopy__(self, memo):
        return self

    def __or__(self, other):
        return typing.Union[self, other]

    def __ror__(self, other):
        return typing.Union[other, self]

typing-extensions 모듈에 백포트가 있지만, 그 동작은 이 PEP의 현재 버전과 정확히 일치하지는 않습니다.

기각된 아이디어

NotGiven = object() 사용

이 방식은 Rationale 섹션에서 언급한 모든 단점을 안고 있습니다.

MISSING 또는 Sentinel 같은 단일한 새 센티널 값 추가

그러한 값은 여러 곳에서 여러 용도로 사용될 수 있으므로, 일부 사용 사례에서 그것이 결코 유효한 값이 되지 않으리라고 항상 확신할 수는 없습니다. 반면에, 전용이며 고유한 센티널 값은 잠재적인 경계 사례를 고려할 필요 없이 확신을 가지고 사용할 수 있습니다.

또한 센티널 값이 사용되는 맥락에 맞는 의미 있는 이름과 repr을 제공할 수 있다는 점도 유용합니다.

마지막으로, 이 방안은 설문 조사 [4]에서 전체 표의 12%만 얻을 정도로 매우 인기가 없었습니다.

기존 Ellipsis 센티널 값 사용

이는 Ellipsis의 원래 의도된 용도가 아닙니다. 다만 pass를 사용하는 대신 빈 클래스나 함수 블록을 정의하는 데 Ellipsis를 사용하는 경우가 점점 흔해지고 있습니다.

또한 잠재적인 새 단일 센티널 값과 마찬가지로, Ellipsis는 전용이며 고유한 값과 달리 모든 경우에 확신을 가지고 사용할 수는 없습니다.

단일 값 열거형 사용

제안된 관용구는 다음과 같습니다:

class NotGivenType(Enum):
    NotGiven = 'NotGiven'
NotGiven = NotGivenType.NotGiven

과도한 반복 외에도 repr이 지나치게 깁니다: <NotGivenType.NotGiven: 'NotGiven'>. 더 짧은 repr을 정의할 수는 있지만, 그 대가로 코드가 조금 더 늘어나고 반복도 더 많아집니다.

마지막으로, 이 선택지는 설문 [4]의 아홉 가지 선택지 중 가장 인기가 없었으며, 한 표도 받지 못한 유일한 선택지였습니다.

센티널 클래스 데코레이터

제안된 관용구는 다음과 같습니다:

@sentinel
class NotGivenType: pass
NotGiven = NotGivenType()

이 방식으로 데코레이터를 매우 간단하고 명확하게 구현할 수 있지만, 이 관용구는 너무 장황하고 반복적이며 기억하기 어렵습니다.

클래스 객체 사용하기

클래스는 본질적으로 싱글턴이므로, 클래스를 센티널 값으로 사용하는 것은 합리적이며 간단한 구현을 가능하게 합니다.

가장 단순한 형태는 다음과 같습니다:

class NotGiven: pass

명확한 repr을 갖추려면 메타클래스를 사용해야 합니다:

class NotGiven(metaclass=SentinelMeta): pass

… 또는 클래스 데코레이터를 사용해야 합니다:

@Sentinel
class NotGiven: pass

클래스를 이런 식으로 사용하는 것은 흔하지 않으며 혼란을 줄 수 있습니다. 주석이 없으면 코드의 의도를 이해하기 어려울 것입니다. 또한 이러한 센티널이 호출 가능 객체가 되는 것과 같은, 예상치 못한 바람직하지 않은 동작을 하게 될 것입니다.

구현은 제공하지 않고 권장 “표준” 관용구만 정의하기

흔히 쓰이는 기존 관용구는 대부분 중대한 단점이 있습니다. 지금까지 이러한 단점을 피하면서도 명확하고 간결한 관용구는 발견되지 않았습니다.

또한 이 주제에 관한 설문 [4]에서 관용구를 권장하는 선택지들은 인기가 없었으며, 가장 많은 표를 받은 선택지도 투표자의 25%만이 선택했습니다.

새로운 표준 라이브러리 모듈 사용하기

이전 초안에서는 새로운 sentinels 또는 sentinellib 모듈에 Sentinel 클래스를 추가하는 것을 제안했습니다. 그러나 공개 호출 가능 객체 하나를 위해 새 모듈을 추가하는 것은 불필요하며, 모듈을 사용하면 이 기능이 기존의 object() 관용구보다 덜 편리해집니다. 운영 위원회(Steering Council) 역시 이 기능을 최소한 object()만큼 쉽게 사용할 수 있도록 내장 기능으로 만들 것을 특별히 권장했습니다.

또한 sentinels라는 이름을 사용하면 현재 활발히 사용되고 있는 기존 PyPI 패키지와 충돌하게 됩니다. 다른 모듈 이름을 사용할 수도 있지만, 이 기능을 내장 기능으로 만들면 이름 문제를 완전히 피할 수 있습니다.

모듈별 센티널 이름 레지스트리 사용

이전 초안에서는 센티널 이름을 각 모듈 안에서 고유하게 만드는 방안을 제안했습니다. 그 설계에서는 모듈 이름과 센티널 이름을 키로 하는 프로세스 전역 레지스트리를 사용하여, 같은 모듈에서 sentinel("MISSING")처럼 반복해서 호출하면 같은 객체를 반환했을 것입니다.

이 방식은 동작이 너무 암묵적이라는 이유로 기각되었습니다. 공유 센티널이 필요한 코드는 이미 MISSING = object()로 하고 있는 것처럼 센티널을 명시적으로 정의하고 이름으로 재사용할 수 있습니다. 또한 지역 스코프의 코드는 호출이나 반복마다 새로운 센티널을 원할 수 있으며, sentinel(name)을 반복해서 호출하면 object()를 반복해서 호출할 때처럼 서로 다른 객체를 생성해야 합니다.

레지스트리를 제거하면 구현과 멘탈 모델도 더 단순해집니다. sentinel(name)은 repr이 name인 새로운 고유 객체를 생성합니다.

모듈 이름을 자동으로 알아내거나 전달하기

이전 초안에서는 레지스트리 기반 설계를 지원하기 위해 선택적 module_name 인자를 제안했습니다.

레지스트리를 제거했으므로 핵심 제안에는 더 이상 공개 module_name 인자가 필요하지 않습니다. 다만 pickle이 임포트 가능한 센티널을 모듈과 이름으로 직렬화할 수 있도록, 구현은 TypeVar 및 이와 유사한 헬퍼들처럼 호출한 모듈을 여전히 내부적으로 기록합니다. 이 내부 모듈 이름은 센티널의 repr에 영향을 주지 않습니다. 모듈이나 클래스 이름이 포함된 repr을 원하는 사용자는 sentinel("mymodule.MISSING")처럼 단일 name 인자에 그 이름을 명시적으로 포함할 수 있습니다.

불리언 평가의 사용자 정의 허용

논의에서는 센티널을 명시적으로 참(truthy)이나 거짓(falsy)으로 평가되게 하거나, bool로 변환할 수 없게 만드는 방안을 검토했습니다. 기존 서드파티 센티널 중 일부는 거짓으로 평가되는 동작을 공개 API의 일부로 노출하고 있으며, 여러 참여자는 불리언 문맥에서 예외를 발생시키는 편이 동일성 검사를 더 잘 강제할 것이라고 주장했습니다.

이 PEP는 센티널에 일반 객체의 기본 동작인 참 평가를 부여하고 동일성 검사를 권장함으로써 초기 제안을 더 단순하게 유지합니다. 사용자 정의 불리언 동작은 그로 인해 늘어나는 API와 타입 지정의 복잡성이 감수할 가치가 있다고 판단되면 나중에 고려할 수 있습니다.

타입 어노테이션에서 typing.Literal 사용하기

이 방안은 논의에서 여러 사람이 제안했으며, 이 PEP도 처음에는 이 방안을 택했습니다. 그러나 예를 들어 Literal["MISSING"]은 센티널 값 MISSING에 대한 전방 참조가 아니라 문자열 값 "MISSING"을 가리키기 때문에 혼란을 일으킬 수 있다는 지적이 있었습니다. 논의에서는 이름만 그대로 사용하는 방안도 자주 제안되었습니다. 이 방식은 None이 확립한 선례와 잘 알려진 패턴을 따르며, 임포트가 필요 없고 훨씬 짧다는 장점이 있습니다.

추가 참고 사항

  • 클래스 스코프에서 정의된 센티널의 경우, 잠재적인 이름 충돌을 피하기 위해서나 정규화된 repr이 더 명확할 때에는 원하는 정규화된 이름을 명시적으로 전달해야 합니다. 예를 들면 다음과 같습니다:
    >>> class MyClass:
    ...    NotGiven = sentinel('MyClass.NotGiven')
    >>> MyClass.NotGiven
    MyClass.NotGiven
    
  • 함수나 메서드 안에서 센티널을 생성하는 것은 허용됩니다. sentinel()을 호출할 때마다 별개의 객체가 생성되므로, 지역 스코프에서 생성한 센티널은 해당 스코프에서 object()를 호출하여 생성한 객체처럼 동작합니다.
  • NotImplemented의 불리언 값은 True이지만, 이를 사용하는 것은 Python 3.9부터 폐지 예정(deprecated)입니다(사용하면 폐지 경고가 발생합니다). 이 폐지 예정 조치는 bpo-35712 [8]에 설명된 것처럼 NotImplemented에 특유한 문제 때문입니다.
  • 서로 관련된 여러 센티널 값을 정의하려면, 특히 그 값들 사이에 정해진 순서가 있을 수 있다면, 대신 Enum이나 그와 비슷한 것을 사용해야 합니다.
  • typing-sig 메일링 리스트 [9]에서 이러한 센티널의 타입 지정에 관한 논의가 있었으며, 그 자리에서 여러 선택지가 논의되었습니다.

각주