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 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

개요

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

근거

많은 프로그램이 나중에 더 큰 숫자를 다뤄야 할 필요를 느끼게 되는데, 알고리즘을 나중에 바꾸는 것은 번거롭습니다. 필요하든 필요하지 않든 모든 산술 연산이 롱 정수를 사용해 수행되는 일반적인 경우, 이는 성능을 저해할 수 있습니다.

기계의 워드 크기가 언어에 노출되는 것은 이식성을 저해합니다. 예를 들어, 이 때문에 파이썬 소스 파일과 .pyc 파일은 32비트 기계와 64비트 기계 사이에서 이식되지 않습니다.

대부분의 애플리케이션에서 무관한 불필요한 세부 사항을 파이썬 사용자로부터 감추고자 하는 일반적인 바람도 있습니다. 그 예로 메모리 할당을 들 수 있는데, 이는 C에서는 명시적이지만 파이썬에서는 자동으로 처리되어, 문자열이나 리스트 등에 크기 제한이 없는 편의성을 제공합니다. 이러한 편의성을 숫자에도 확장하는 것이 타당합니다.

이는 새로운 파이썬 프로그래머(프로그래밍 자체를 처음 접하는지 여부와 무관하게)가 이 언어를 사용하기 시작하기 전에 배워야 할 것을 하나 줄여줄 것입니다.

구현

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

  1. C long을 위한 PyInt 타입의 슬롯은 다음과 같이 바뀔 것입니다.:
    union {
        long i;
        struct {
            unsigned long length;
            digit digits[1];
        } bignum;
    };
    

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

  2. 기존의 short int와 long int 타입은 그대로 유지되지만, 결과가 short int로 표현될 수 없는 경우 연산은 OverflowError를 일으키는 대신 long int를 반환합니다. integer라는 새로운 타입이 도입될 수 있는데, 이는 intlong 구현 타입이 모두 서브클래싱하는 추상 베이스 타입입니다. 이는 프로그램이 단일 테스트로 정수 여부를 확인할 수 있어 유용합니다:
    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 모듈, 그리고 picklecPickle 모듈에서 서로 다르게 처리됩니다. 이 차이는 (적어도 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을 다루는 것에 대해 걱정할 필요가 없습니다.

전환

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

  1. 현재 OverflowError를 일으키는 짧은 정수 연산은 대신 긴 정수 값을 반환합니다. 이것이 이 단계에서의 유일한 변경 사항입니다. 리터럴은 여전히 짧은 정수와 긴 정수를 구별합니다. 위에 나열된 다른 의미상의 차이점들(<<의 동작을 포함하여)은 그대로 유지됩니다. 이 단계는 현재 OverflowError를 발생시키는 상황만을 변경하기 때문에, 기존 코드를 깨뜨리지 않을 것으로 가정됩니다. (이 예외에 의존하는 코드는 신경 쓸 필요가 없을 정도로 지나치게 복잡할 수밖에 없을 것입니다.) 극도의 하위 호환성을 우려하는 사람들을 위해, 명령줄 옵션(또는 warnings 모듈 호출)을 통해 이 시점에서 경고나 오류를 발생시킬 수 있도록 하되, 기본값으로는 꺼져 있습니다.
  2. 남은 의미상의 차이점들이 다루어집니다. 모든 경우에 긴 정수의 의미가 우선합니다. 이는 일부 오래된 코드를 깨뜨리는 하위 호환성 문제를 일으키기 때문에, 이 단계에서는 future 문 그리고/또는 경고, 그리고 장기간의 전환 단계가 필요할 수 있습니다. 끝에 붙는 L은 입력값과 repr()에서 롱형에 계속 사용될 것입니다.
    1. 2B 단계에서 수치 결과가 달라지는 연산에 대해 경고가 활성화되며, 특히 hex()oct(), %u, %x, %X%o, [sys.maxint+1, sys.maxint*2+1] 범위(양 끝 포함)에 있는 hexoct 리터럴, 그리고 비트를 잃는 왼쪽 시프트가 해당됩니다.
    2. 이 연산들에 대한 새로운 의미가 구현됩니다. 이전과 다른 결과를 내는 연산은 경고를 발생시키지 않습니다.
  3. 뒤에 붙는 Lrepr()에서 제거되고, 입력 시에는 허용되지 않게 됩니다. (가능하다면 long 타입은 완전히 사라집니다.) 뒤에 붙는 Lhex()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에 있습니다).