PEP 237 – 롱 정수와 정수의 통합
- Author:
- Moshe Zadka, Guido van Rossum
- Status:
- Final
- Type:
- Standards Track
- Created:
- 11-Mar-2001
- Python-Version:
- 2.2
- Post-History:
- 16-Mar-2001, 14-Aug-2001, 23-Aug-2001
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
개요
파이썬은 현재 두 종류의 정수(int)를 구분하는데, C의 long 크기(일반적으로 32비트 또는 64비트)로 제한되는 일반 정수 또는 짧은 정수와, 사용 가능한 메모리에 의해서만 제한되는 롱 정수가 있습니다. 짧은 정수에 대한 연산이 C의 long에 들어맞지 않는 결과를 낳으면 에러가 발생합니다. 그 밖에도 몇 가지 구분이 존재합니다. 이 PEP는 의미론상의 차이 대부분을 없애서, 파이썬 사용자의 관점에서 이 두 타입을 통합할 것을 제안합니다.
근거
많은 프로그램이 나중에 더 큰 숫자를 다뤄야 할 필요를 느끼게 되는데, 알고리즘을 나중에 바꾸는 것은 번거롭습니다. 필요하든 필요하지 않든 모든 산술 연산이 롱 정수를 사용해 수행되는 일반적인 경우, 이는 성능을 저해할 수 있습니다.
기계의 워드 크기가 언어에 노출되는 것은 이식성을 저해합니다. 예를 들어, 이 때문에 파이썬 소스 파일과 .pyc 파일은 32비트 기계와 64비트 기계 사이에서 이식되지 않습니다.
대부분의 애플리케이션에서 무관한 불필요한 세부 사항을 파이썬 사용자로부터 감추고자 하는 일반적인 바람도 있습니다. 그 예로 메모리 할당을 들 수 있는데, 이는 C에서는 명시적이지만 파이썬에서는 자동으로 처리되어, 문자열이나 리스트 등에 크기 제한이 없는 편의성을 제공합니다. 이러한 편의성을 숫자에도 확장하는 것이 타당합니다.
이는 새로운 파이썬 프로그래머(프로그래밍 자체를 처음 접하는지 여부와 무관하게)가 이 언어를 사용하기 시작하기 전에 배워야 할 것을 하나 줄여줄 것입니다.
구현
처음에는 두 가지 대안적 구현이 제안되었습니다(각 저자가 하나씩 제안):
- C long을 위한
PyInt타입의 슬롯은 다음과 같이 바뀔 것입니다.:union { long i; struct { unsigned long length; digit digits[1]; } bignum; };
long의 하위n-1비트만 의미가 있으며, 최상위 비트는 항상 설정되어 있습니다. 이렇게 하면union을 구별할 수 있습니다. 모든PyInt함수는 어떤 유형의 연산을 사용할지 결정하기 전에 이 비트를 확인합니다. - 기존의 short int와 long int 타입은 그대로 유지되지만, 결과가 short int로 표현될 수 없는 경우 연산은
OverflowError를 일으키는 대신 long int를 반환합니다.integer라는 새로운 타입이 도입될 수 있는데, 이는int와long구현 타입이 모두 서브클래싱하는 추상 베이스 타입입니다. 이는 프로그램이 단일 테스트로 정수 여부를 확인할 수 있어 유용합니다:if isinstance(i, integer): ...
다소 고려한 끝에, 두 번째 구현 계획이 선택되었는데, 이는 구현하기가 훨씬 쉽고, C API 수준에서 하위 호환성을 유지하며, 게다가 과도기적 조치로서 부분적으로 구현할 수도 있기 때문입니다.
비호환성
다음 연산들은 짧은 정수와 긴 정수에 대해 (보통 미묘하게) 다른 의미를 가지며, 둘 중 하나는 어떻게든 변경되어야 합니다. 이는 빠짐없는 목록을 의도한 것입니다. 동일한 값을 가진 짧은 정수 또는 긴 정수 중 무엇이 전달되는지에 따라 결과가 달라지는 다른 연산을 알고 있다면, 두 번째 저자에게 편지를 보내 주십시오.
- 현재 짧은 정수에 대한 모든 산술 연산자는
<<를 제외하고 결과를 짧은 정수로 표현할 수 없는 경우OverflowError를 발생시킵니다. 이는 대신 긴 정수를 반환하도록 변경될 것입니다. 다음 연산자들은 현재OverflowError를 발생시킬 수 있습니다:x+y,x-y,x*y,x**y,divmod(x, y),x/y,x%y,-x. (마지막 네 개는-sys.maxint-1값이 관련될 때만 오버플로가 발생할 수 있습니다.) - 현재
x<<n은 short int에 대해 비트를 잃을 수 있습니다. short int를 반환하면 비트를 잃게 되는 경우(부호가 바뀌는 것도 비트를 잃는 것의 특수한 경우로 간주합니다), 시프트되어 빠져나간 비트를 모두 포함하는 long int를 반환하도록 변경될 예정입니다. - 현재 short int에 대한 16진수 및 8진수 리터럴은 음수 값을 지정할 수 있습니다. 예를 들어 32비트 머신에서
0xffffffff == -1입니다. 이는0xffffffffL(2**32-1)와 같아지도록 변경될 예정입니다. - 현재
%u,%x,%X,%o문자열 포맷 연산자와hex(),oct()내장 함수는 음수에 대해 다르게 동작합니다. 음수인 short int는 부호 없는 C long으로 포맷되지만, 음수인 long int는 마이너스 기호와 함께 포맷됩니다. 이는 모든 경우에 long int 의미론을 사용하도록 변경될 예정입니다(다만 현재 long int에 대한hex()와oct()의 출력을 구분해 주는 끝의 L은 붙지 않습니다). 이는 곧%u가%d의 별칭이 된다는 것을 의미한다는 점에 유의하십시오. 이는 결국 제거될 예정입니다. - 현재 long int의
repr()은 L로 끝나는 문자열을 반환하지만, short int의repr()은 그렇지 않습니다. L은 삭제될 예정이지만, Python 3.0 이전에는 아닙니다. - 현재 long 피연산자를 사용하는 연산은 절대 short int를 반환하지 않습니다. 이는 일부 최적화를 가능하게 하므로 변경될 수도 있습니다. (이 영역에는 아직 어떠한 변경도 이루어지지 않았으며, 앞으로도 계획되어 있지 않습니다.)
type(x).__name__표현식은 x가 short int인지 long int인지에 따라 달라집니다. 구현 대안 2가 선택되었으므로, 이 차이는 그대로 남아 있을 예정입니다. (Python 3.0에서는 이 차이를 숨기는 트릭을 적용할 수도 있는데, 사용자 코드에 이 차이를 드러내는 것은 실제로 성가신 일이며, 두 타입 간의 차이가 덜 드러날수록 더욱 그러하기 때문입니다.)- long int와 short int는
marshal모듈, 그리고pickle과cPickle모듈에서 서로 다르게 처리됩니다. 이 차이는 (적어도 Python 3.0까지는) 그대로 남아 있을 예정입니다. - 값이 작은 short int(일반적으로 -1에서 99 사이)는 인턴됩니다 – 결과가 그런 값을 가질 때마다 같은 값을 가진 기존 short int가 반환됩니다. 이는 같은 값을 가진 long int에는 적용되지 않습니다. 이 차이는 그대로 유지됩니다. (이러한 인터닝이 보장되지는 않으므로, 이것이 의미상의 차이인지는 논쟁의 여지가 있습니다 – 하지만 short int 비교에
is를 사용하며 이러한 인터닝 덕분에 우연히 동작하는 코드가 존재할 수 있습니다. 그런 코드는 long int와 함께 사용하면 실패할 수 있습니다.)
리터럴
정수 리터럴 끝의 L은 더 이상 아무 의미도 갖지 않게 되며, 결국에는 허용되지 않게 됩니다. 컴파일러는 오직 값만을 기준으로 적절한 타입을 선택합니다. (Python 3.0 전까지는 리터럴을 강제로 long으로 만들지만, 끝에 L이 없는 리터럴도 short int로 표현할 수 없다면 long이 될 수 있습니다.)
내장 함수
함수 int()는 인자 값에 따라 short int나 long int를 반환합니다. Python 3.0에서는 함수 long()이 함수 int()를 호출하지만, 그 전까지는 결과를 강제로 long int로 만드는 것을 계속하며, 그 외에는 int()와 동일하게 동작합니다. 내장 이름 long은 (Python 3.0에서 완전히 제거되지 않는 한) long 구현 타입을 나타내기 위해 언어에 남아 있겠지만, 필요할 때 자동으로 long을 반환하므로 int() 함수를 사용하는 것이 여전히 권장됩니다.
C API
C API는 변경되지 않으며, C 코드는 여전히 short int와 long int의 차이를 알고 있어야 합니다. (Python 3.0의 C API는 아마도 완전히 호환되지 않을 것입니다.)
PyArg_Parse*() API는 C int나 long으로 표현 가능한 범위 내에 있는 한 이미 long int를 허용하므로, C int나 long 인자를 받는 함수는 Python long을 다루는 것에 대해 걱정할 필요가 없습니다.
전환
전환에는 세 가지 주요 단계가 있습니다:
- 현재
OverflowError를 일으키는 짧은 정수 연산은 대신 긴 정수 값을 반환합니다. 이것이 이 단계에서의 유일한 변경 사항입니다. 리터럴은 여전히 짧은 정수와 긴 정수를 구별합니다. 위에 나열된 다른 의미상의 차이점들(<<의 동작을 포함하여)은 그대로 유지됩니다. 이 단계는 현재OverflowError를 발생시키는 상황만을 변경하기 때문에, 기존 코드를 깨뜨리지 않을 것으로 가정됩니다. (이 예외에 의존하는 코드는 신경 쓸 필요가 없을 정도로 지나치게 복잡할 수밖에 없을 것입니다.) 극도의 하위 호환성을 우려하는 사람들을 위해, 명령줄 옵션(또는 warnings 모듈 호출)을 통해 이 시점에서 경고나 오류를 발생시킬 수 있도록 하되, 기본값으로는 꺼져 있습니다. - 남은 의미상의 차이점들이 다루어집니다. 모든 경우에 긴 정수의 의미가 우선합니다. 이는 일부 오래된 코드를 깨뜨리는 하위 호환성 문제를 일으키기 때문에, 이 단계에서는 future 문 그리고/또는 경고, 그리고 장기간의 전환 단계가 필요할 수 있습니다. 끝에 붙는 L은 입력값과
repr()에서 롱형에 계속 사용될 것입니다.- 2B 단계에서 수치
결과가 달라지는 연산에 대해 경고가 활성화되며, 특히
hex()와oct(),%u,%x,%X와%o,[sys.maxint+1, sys.maxint*2+1]범위(양 끝 포함)에 있는hex와oct리터럴, 그리고 비트를 잃는 왼쪽 시프트가 해당됩니다. - 이 연산들에 대한 새로운 의미가 구현됩니다. 이전과 다른 결과를 내는 연산은 경고를 발생시키지 않습니다.
- 2B 단계에서 수치
결과가 달라지는 연산에 대해 경고가 활성화되며, 특히
- 뒤에 붙는 L은
repr()에서 제거되고, 입력 시에는 허용되지 않게 됩니다. (가능하다면long타입은 완전히 사라집니다.) 뒤에 붙는 L은hex()와oct()에서도 제거됩니다.
Phase 1은 Python 2.2에서 구현됩니다.
Phase 2는 점진적으로 구현되며, 2A는 Python 2.3에서, 2B는 Python 2.4에서 구현됩니다.
Phase 3은 Python 3.0에서 구현됩니다(Python 2.4가 릴리스된 후 최소 2년 후).
OverflowWarning
다음은 현재 OverflowError를 발생시키는 상황에서 생성되는 경고를 규율하는 규칙입니다. 이는 전환 단계 1에 적용됩니다. 역사적 참고 사항: 단계 1은 Python 2.2에서, 단계 2A는 Python 2.3에서 완료되었음에도 불구하고, Python 2.3에서 OverflowWarning이 여전히 생성되고 있다는 사실을 아무도 알아차리지 못했습니다. 결국 Python 2.4에서 비활성화되었습니다. Python 내장 OverflowWarning과 이에 대응하는 C API PyExc_OverflowWarning은 Python 2.4에서 더 이상 생성되거나 사용되지 않지만, (가능성은 낮지만) 사용자 코드가 이를 사용하는 경우를 대비해 Python 2.5까지 남아 있게 됩니다.
- 새로운 경고 범주인
OverflowWarning이 도입됩니다. 이는 내장 이름입니다. - int 결과가 오버플로되면
OverflowWarning경고가 발생하며, 연산을 나타내는 메시지 인자가 함께 제공됩니다(예: “integer addition”). 이는sys.stderr에 경고 메시지를 표시할 수도 있고, 예외를 발생시킬 수도 있으며, 이 모든 것은-W명령줄 옵션과 warnings 모듈에 의해 제어됩니다. OverflowWarning경고는 기본적으로 무시됩니다.OverflowWarning경고는 다른 모든 경고와 마찬가지로-W명령줄 옵션이나warnings.filterwarnings()호출을 통해 제어할 수 있습니다. 예를 들어:python -Wdefault::OverflowWarning
OverflowWarning이 특정 소스 라인에서 처음 발생했을 때 표시되도록 하며,:python -Werror::OverflowWarning
OverflowWarning이 발생할 때마다 예외로 전환되도록 합니다. 다음 코드는 프로그램 내부에서 이 경고를 활성화합니다:import warnings warnings.filterwarnings("default", "", OverflowWarning)
-W옵션에 대해서는 파이썬man페이지를,filterwarnings()에 대해서는warnings모듈 문서를 참조하십시오.OverflowWarning경고가 오류로 전환되면OverflowError로 대체됩니다. 이는 하위 호환성을 위해 필요합니다.- 경고가 예외로 전환되지 않는 한, 연산 결과(예:
x+y)는 인자들을 long int로 변환한 뒤 다시 계산됩니다.
예제
long int를 정수를 받는 C 함수나 내장 연산에 전달하면, 값이 맞는 한(PyArg_ParseTuple()이 구현된 방식 덕분에) short int와 동일하게 취급됩니다. long 값이 맞지 않으면 여전히 OverflowError가 발생합니다. 예를 들어:
def fact(n):
if n <= 1:
return 1
return n*fact(n-1)
A = "ABCDEFGHIJKLMNOPQ"
n = input("Gimme an int: ")
print A[fact(n)%17]
n >= 13인 경우, 계산된 인덱스가 항상 range(17) 범위 안에 있음에도 불구하고, 현재는 (사용자가 입력에 뒤따르는 L을 붙이지 않는 한) OverflowError가 발생합니다. 새로운 방식에서는 이 코드가 올바르게 동작합니다: 인덱스는 long int로 계산되지만 그 값은 범위 안에 있게 됩니다.
해결된 문제
이전에 열려 있던 이 문제들은 해결되었습니다.
- long에 적용된
hex()와oct()는 Python 3000까지 계속 끝에 L을 출력할 것입니다. 위의 원문은 이 점을 명확히 설명하지 않았지만, Python 2.4에서 이런 일이 발생하지 않았으므로 그대로 두는 것이 낫다고 판단되었습니다. 여기 BDFL의 선언이 있습니다:https://mail.python.org/pipermail/python-dev/2006-June/065918.html
sys.maxint는 어떻게 해야 할까요? 값의 타입을 검사할 때처럼 short int와 long int의 구분이 여전히 유의미한 경우에는 여전히 의미가 있으므로, 그대로 남겨 두십시오.%u를 완전히 제거해야 할까요? 제거하십시오.<<가 정수를 잘라내지 않는다는 것에 대해 경고해야 할까요? 예.- 오버플로 경고가 이식 가능한 최대 크기를 기준으로 해야 할까요? 아니오.
구현
Python 2.x 계열의 구현 작업은 완료되었습니다. 1단계는 Python 2.2와 함께 배포되었고, 2A단계는 Python 2.3과 함께 배포되었으며, 2B단계는 Python 2.4와 함께 배포될 예정입니다(이미 CVS에 있습니다).
Copyright
This document has been placed in the public domain.