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

Python 개선 제안 한국어 번역

PEP 535 – 풍부한 비교 연쇄

Author:
Alyssa Coghlan <ncoghlan at gmail.com>
Status:
Deferred
Type:
Standards Track
Requires:
532
Created:
12-Nov-2016
Python-Version:
3.8

Table of Contents

번역·라이선스 안내

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

PEP 연기

이 PEP에 대한 추가 고려는 빠르더라도 Python 3.8까지 연기되었습니다.

초록

이 PEP는 PEP 335에서 영감을 받고, PEP 532에 설명된 회로 차단 프로토콜을 기반으로 하여, 왼쪽 비교가 그 결과로 회로 차단기를 반환하는 경우 비교 연결에 논리합 연산자 (and) 대신 좌결합 회로 차단 연산자 (else)를 사용하도록 연쇄 비교의 정의를 변경할 것을 제안합니다.

NumPy에서 현재 단일 값 배열을 처리하는 방식에서 비롯되는 몇 가지 실질적인 복잡성이 있지만, 이 변경으로 행렬에 대해 원소별 연쇄 비교 연산을 수행할 수 있어야 합니다. 이 경우 결과는 불리언 값의 행렬이 되며, ValueError를 발생시키거나 비어 있지 않은 행렬임을 나타내며 항진적으로 True를 반환하지 않습니다.

다른 PEP와의 관계

이 PEP는 도입에 필수적인 부분이라기보다 회로 차단 프로토콜의 후속 사용 사례로서 PEP 532의 초기 버전들에서 추출되었습니다.

비교 연쇄의 의미론적 정의를 변경하여 원소별 비교 사용 사례를 처리하자는 이 PEP의 구체적인 제안은 Guido가 PEP 335를 거부한 데서 직접 도출되었습니다.

사양

0 < x < 10과 같은 연쇄 비교식은 다음과 같이 작성됩니다.:

LEFT_BOUND LEFT_OP EXPR RIGHT_OP RIGHT_BOUND

현재 의미상 대략 다음과 동등합니다.:

_expr = EXPR
_lhs_result = LEFT_BOUND LEFT_OP _expr
_expr_result = _lhs_result and (_expr RIGHT_OP RIGHT_BOUND)

이 PEP는 PEP 532에서 도입된 회로 차단 개념을 사용하여, 왼쪽 비교가 회로 차단기를 반환하는지 명시적으로 확인하고, 그러한 경우 비교 연결을 구현하는 데 and 대신 else를 사용하도록 비교 연결을 변경할 것을 제안합니다.:

_expr = EXPR
_lhs_result = LEFT_BOUND LEFT_OP _expr
if hasattr(type(_lhs_result), "__else__"):
    _expr_result = _lhs_result else (_expr RIGHT_OP RIGHT_BOUND)
else:
    _expr_result = _lhs_result and (_expr RIGHT_OP RIGHT_BOUND)

이를 통해 NumPy 배열과 같은 타입은 비교 연산에서 적절히 정의된 회로 차단기를 반환하여 연쇄 비교의 동작을 제어할 수 있습니다.

이 논리를 임의의 수의 연쇄 비교 연산으로 확장하는 방식은 and에 대해 기존에 확장하는 방식과 동일합니다.

근거

결국 PEP 335를 거부하면서, Guido van Rossum은 다음과 같이 언급했습니다 [1]:

NumPy 관계자들은 다소 별개의 문제를 제기했습니다: 그들에게 가장 흔한 사용 사례는 연쇄 비교(예: A < B < C)입니다.

이 관찰을 이해하려면, 먼저 NumPy 배열에서 비교가 어떻게 동작하는지 살펴봐야 합니다:

>>> import numpy as np
>>> increasing = np.arange(5)
>>> increasing
array([0, 1, 2, 3, 4])
>>> decreasing = np.arange(4, -1, -1)
>>> decreasing
array([4, 3, 2, 1, 0])
>>> increasing < decreasing
array([ True,  True, False, False, False], dtype=bool)

여기서 NumPy 배열 비교는 기본적으로 요소별로 이루어지며, 왼쪽 배열의 각 요소를 오른쪽 배열의 대응하는 요소와 비교하여 불리언 결과의 행렬을 생성한다는 것을 알 수 있습니다.

비교의 어느 한쪽이 스칼라 값이면, 이는 배열 전체에 브로드캐스트되어 각 개별 요소와 비교됩니다:

>>> 0 < increasing
array([False,  True,  True,  True,  True], dtype=bool)
>>> increasing < 4
array([ True,  True,  True,  True, False], dtype=bool)

그러나 이 브로드캐스트 관용구는 연쇄 비교를 사용하려고 하면 깨집니다:

>>> 0 < increasing < 4
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

문제는 내부적으로 Python이 이 연쇄 비교를 암묵적으로 다음 형태로 확장한다는 것입니다:

>>> 0 < increasing and increasing < 4
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

그리고 NumPy는 a.any()a.all()이 동일한 결과를 갖는 것이 보장되는 단일 요소 배열에 대해서만 불리언 값으로의 암묵적 강제 변환을 허용합니다:

>>> np.array([False]) and np.array([False])
array([False], dtype=bool)
>>> np.array([False]) and np.array([True])
array([False], dtype=bool)
>>> np.array([True]) and np.array([False])
array([False], dtype=bool)
>>> np.array([True]) and np.array([True])
array([ True], dtype=bool)

이 PEP의 제안은 NumPy의 요소별 비교 연산 정의를 업데이트하여, 새로운 회로 차단 프로토콜을 구현하는 전용 서브클래스를 반환하도록 하고, 결과 배열이 불리언 맥락에서 해석될 때 항상 False를 반환하여 결코 단락 평가 동작을 유발하지 않도록 변경함으로써 이 상황을 바꿀 수 있게 해줄 것입니다:

class ComparisonResultArray(np.ndarray):
    def __bool__(self):
        # Element-wise comparison chaining never short-circuits
        return False
    def _raise_NotImplementedError(self):
        msg = ("Comparison array truth values are ambiguous outside "
               "chained comparisons. Use a.any() or a.all()")
        raise NotImplementedError(msg)
    def __not__(self):
        self._raise_NotImplementedError()
    def __then__(self, result):
        self._raise_NotImplementedError()
    def __else__(self, result):
        return np.logical_and(self, other.view(ComparisonResultArray))

이 변경을 통해, 위의 연쇄 비교 예제는 다음을 반환할 수 있게 됩니다:

>>> 0 < increasing < 4
ComparisonResultArray([ False,  True,  True,  True, False], dtype=bool)

구현

실제 구현은 PEP 532에서 제안된 변경을 적용하는 아이디어에 대한 원칙적 관심 여부가 확인될 때까지 보류되었습니다.

…TBD…

참고 문헌