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

Python 개선 제안 한국어 번역

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

Table of Contents

번역·라이선스 안내

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

초록

현재 Python은 두 종류의 정수(int)를 구분합니다. 하나는 C long의 크기(보통 32비트 또는 64비트)로 제한되는 일반 정수, 곧 짧은 정수이고, 다른 하나는 사용 가능한 메모리에 의해서만 제한되는 긴 정수입니다. 짧은 정수에 대한 연산 결과가 C long에 들어가지 않으면 오류가 발생합니다. 이 밖에도 몇 가지 차이점이 있습니다. 이 PEP는 의미상의 차이를 대부분 없애 Python 사용자 관점에서 두 타입을 통합할 것을 제안합니다.

근거

많은 프로그램이 나중에야 더 큰 수를 다뤄야 한다는 것을 알게 되며, 그때 가서 알고리즘을 바꾸는 것은 번거롭습니다. 필요 여부와 관계없이 모든 산술 연산을 긴 정수로 수행하면 일반적인 경우에 성능이 떨어질 수 있습니다.

기계 워드 크기가 언어에 드러나면 이식성이 떨어집니다. 예를 들어 이 때문에 Python 소스 파일과 .pyc 파일은 32비트 기계와 64비트 기계 사이에서 이식할 수 없습니다.

또한 대부분의 애플리케이션과 관계없는 불필요한 세부 사항은 Python 사용자에게 숨기고 싶다는 일반적인 바람도 있습니다. 메모리 할당이 한 예입니다. 메모리 할당은 C에서는 명시적이지만 Python에서는 자동으로 이루어지므로, 문자열이나 리스트 등의 크기에 제한이 없다는 편리함을 누릴 수 있습니다. 이 편리함을 숫자로 확장하는 것은 타당합니다.

이렇게 하면 새로 Python을 배우는 프로그래머(프로그래밍 자체가 처음이든 아니든)가 언어를 사용하기 전에 배워야 할 것이 하나 줄어듭니다.

구현

처음에는 두 가지 대안 구현이 제안되었습니다(저자마다 하나씩).

  1. PyInt 타입의 C long용 슬롯은 다음으로 바뀝니다:
    union {
        long i;
        struct {
            unsigned long length;
            digit digits[1];
        } bignum;
    };
    

    long의 하위 n-1 비트만 의미가 있으며, 최상위 비트는 항상 설정되어 있습니다. 이 비트로 union을 구별합니다. 모든 PyInt 함수는 어떤 종류의 연산을 사용할지 결정하기 전에 이 비트를 확인합니다.

  2. 기존의 짧은 정수 타입과 긴 정수 타입은 그대로 유지되지만, 결과를 짧은 정수로 표현할 수 없을 때 연산은 OverflowError를 발생시키는 대신 긴 정수를 반환합니다. 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에서는 이 차이를 숨기는 기법을 도입할 수 있을지도 모릅니다. 사용자 코드에 이 차이가 드러나는 것은 실제로 성가신 일이며, 두 타입 간의 차이가 덜 눈에 띌수록 더욱 그렇기 때문입니다.)
  • marshal 모듈과 pickle 및 cPickle 모듈은 long int와 short int를 다르게 처리합니다. 이 차이는 (적어도 Python 3.0까지는) 유지됩니다.
  • 작은 값(일반적으로 -1 이상 99 이하)을 가진 short int는 인터닝됩니다 – 결과가 그러한 값을 가질 때마다 같은 값을 가진 기존 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은 long 구현 타입을 나타내기 위해 언어에 남아 있겠지만(Python 3.0에서 완전히 제거되지 않는 한), 필요할 때 자동으로 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을 처리하는 문제를 걱정할 필요가 없습니다.

전환

전환에는 세 가지 주요 단계가 있습니다:

  1. 현재 OverflowError를 발생시키는 short int 연산은 대신 long int 값을 반환합니다. 이것이 이 단계의 유일한 변경 사항입니다. 리터럴은 여전히 short int와 long int를 구분합니다. 위에 나열한 다른 의미상의 차이(<<의 동작 포함)는 그대로 유지됩니다. 이 단계는 현재 OverflowError를 발생시키는 상황만 변경하므로 기존 코드를 망가뜨리지 않을 것으로 가정합니다. (이 예외에 의존하는 코드라면 신경 쓸 필요가 없을 만큼 지나치게 난해한 코드일 것입니다.) 하위 호환성을 극도로 중시하는 사람들을 위해 명령줄 옵션(또는 warnings 모듈 호출)으로 이 시점에 경고나 오류가 발생하도록 할 수 있지만, 이 기능은 기본적으로 꺼져 있습니다.
  2. 남아 있는 의미상의 차이를 해결합니다. 모든 경우에 long int의 의미가 우선합니다. 이로 인해 일부 오래된 코드를 망가뜨리는 하위 호환성 문제가 생기므로, 이 단계에서는 future 문이나 경고 또는 둘 다가 필요할 수 있으며 긴 전환 기간이 필요할 수도 있습니다. 끝에 붙는 L은 입력에서, 그리고 repr()에서 long을 나타내는 데 계속 사용됩니다.
    1. 2B 단계에서 수치 결과가 바뀌게 될 연산, 특히 hex()와 oct(), %u, %x, %X와 %o, (경계값을 포함하는) [sys.maxint+1, sys.maxint*2+1] 범위의 hex 및 oct 리터럴, 그리고 비트를 잃는 왼쪽 시프트에 대해 경고가 활성화됩니다.
    2. 이 연산들에 대한 새로운 의미가 구현됩니다. 이전과 다른 결과를 내는 연산은 경고를 발생시키지 않습니다.
  3. 끝에 붙는 L은 repr()에서 제거되며 입력에서는 허용되지 않습니다. (가능하다면 long 타입은 완전히 사라집니다.) 끝에 붙는 L은 hex()와 oct()에서도 제거됩니다.

단계 1은 Python 2.2에서 구현됩니다.

단계 2는 점진적으로 구현되며, 2A는 Python 2.3에서, 2B는 Python 2.4에서 구현됩니다.

단계 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 옵션에 대해서는 python man 페이지를, filterwarnings()에 대해서는 warnings 모듈 문서를 참고하십시오.

  • OverflowWarning 경고가 오류로 바뀌면 OverflowError가 대신 발생합니다. 이는 하위 호환성을 위해 필요합니다.
  • 경고가 예외로 바뀌지 않는 한, 연산 결과(예: x+y)는 인자를 long int로 변환한 뒤 다시 계산됩니다.

예시

정수를 받는 C 함수나 내장 연산에 long int를 전달하면, 값이 범위에 들어맞는 한 short int와 똑같이 처리됩니다(PyArg_ParseTuple()의 구현 방식 덕분입니다). 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]

계산된 인덱스는 항상 range(17) 안에 있는데도, 현재는 n >= 13이면 (사용자가 입력 끝에 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에 반영되어 있습니다).