PEP 535 – 풍부한 비교 연쇄
- Author:
- Alyssa Coghlan <ncoghlan at gmail.com>
- Status:
- Deferred
- Type:
- Standards Track
- Requires:
- 532
- Created:
- 12-Nov-2016
- Python-Version:
- 3.8
번역·라이선스 안내
이 비공식 한국어 번역은 원문 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에 대한 기존 확장 방식과 같습니다.
근거
Guido van Rossum은 PEP 335를 최종적으로 거부하면서 다음과 같이 언급했습니다 [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에서 제안된 변경 사항을 적용하자는 아이디어에 원칙적인 관심이 모일 때까지 연기되었습니다.
…미정…
참고 문헌
Copyright
This document has been placed in the public domain under the terms of the CC0 1.0 license: https://creativecommons.org/publicdomain/zero/1.0/