PEP 3137 – 불변 바이트와 가변 버퍼
- Author:
- Guido van Rossum <guido at python.org>
- Status:
- Final
- Type:
- Standards Track
- Created:
- 26-Sep-2007
- Python-Version:
- 3.0
- Post-History:
- 26-Sep-2007, 30-Sep-2007
Table of Contents
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
소개
가변 바이트 타입을 포함한 Python 3.0a1을 릴리스한 후, 불변 바이트를 표현할 방법을 추가하라는 압력이 커졌습니다. Gregory P. Smith는 PEP 3118의 새로운 버퍼 API를 사용하여 데이터를 잠그도록 요청함으로써 바이트 객체를 일시적으로 불변으로 만들 수 있게 하는 패치를 제안했습니다. 이는 저에게 올바른 접근 방식으로 보이지 않았습니다.
Jeffrey Yasskin은 Adam Hupp의 도움을 받아 바이트 타입을 불변으로 만들고(모든 변경 API를 조잡하게 제거하여) 테스트 스위트에서 발생한 여파를 수정하는 패치를 준비했습니다. 이를 통해 작은 조각으로 반환 값을 구성하는 코드를 제외하면, 바이트의 가변성에 의존하는 곳이 그다지 많지 않다는 사실이 드러났습니다.
결과를 곰곰이 생각하고, array 모듈을 가변 바이트 타입의 임시 대용으로 사용하는 것이 결코 이상적이지 않다는 점을 알아차렸으며, Talin이 앞서 제시한 제안을 떠올린 끝에, 가변 바이트 타입과 불변 바이트 타입을 모두 두자는 제안을 내놓았습니다. (이 제안은 이전에도 제기되었지만, Jeffrey의 패치가 보여 준 증거를 보기 전까지는 이를 받아들일 마음이 없었습니다.)
또한 가능한 구현 전략이 명확해졌습니다. 즉, 로캘 지원과 Unicode로의 암시적 변환 및 Unicode에서의 암시적 변환을 제거하도록 축소한 기존 PyString 구현을 불변 바이트 타입에 사용하고, 새로운 PyBytes 구현은 가변 바이트 타입으로 유지하는 것입니다.
이어진 논의에서 이 아이디어는 환영받지만 더 정확하게 명세화할 필요가 있다는 점이 분명해졌습니다. 따라서 이 PEP가 마련되었습니다.
장점
불변 바이트 타입을 두는 한 가지 장점은 코드 객체가 이를 사용할 수 있다는 것입니다. 또한 키에 바이트를 사용하여 해시 테이블을 효율적으로 생성할 수 있습니다. 이는 텍스트를 나타내는 바이트를 기반으로 하는 HTTP나 SMTP 같은 프로토콜을 구문 분석할 때 유용할 수 있습니다.
Python 2.x에서 이진 데이터(또는 인코딩된 텍스트)를 조작하는 코드를 포팅하는 작업은 가변 바이트를 사용하는 기존 3.0 설계보다 새로운 설계를 사용할 때 더 쉬워집니다. str을 bytes로 바꾸고 ‘…’ 리터럴을 b’…’ 리터럴로 바꾸기만 하면 됩니다.
이름 지정
Python 수준에서 다음 타입 이름을 제안합니다:
bytes는 불변 바이트 배열입니다(PyString).bytearray는 가변 바이트 배열입니다(PyBytes).memoryview는 다른 객체에 대한 바이트 뷰입니다(PyMemory).
buffer라는 이전 타입은 PEP 3118에서 도입된 새로운 타입 memoryview와 매우 유사하므로 중복됩니다. 이 PEP의 나머지 부분에서는 memoryview의 기능을 논의하지 않습니다. 여기서는 단지 이전 buffer타입을 없애는 것을 정당화하기 위해 언급합니다. (이 PEP의 이전 버전에서는 buffer를 PyBytes의 새 이름으로 제안했지만, 결국 buffer라는 단어가 다른 여러 용도로 사용된다는 점을 고려할 때 이 이름은 혼동을 일으키는 것으로 판단되었습니다.)
장기적으로 C API 이름을 변경하는 것이 합리적이지만, 이 PEP에서는 모두에게 익숙할 기존 C API 이름을 유지합니다.
요약
다음은 다양한 Python 버전에서의 타입 이름을 요약한 간단한 ASCII 아트 표입니다.:
+--------------+-------------+------------+--------------------------+
| C name | 2.x repr | 3.0a1 repr | 3.0a2 repr |
+--------------+-------------+------------+--------------------------+
| PyUnicode | unicode u'' | str '' | str '' |
| PyString | str '' | str8 s'' | bytes b'' |
| PyBytes | N/A | bytes b'' | bytearray bytearray(b'') |
| PyBuffer | buffer | buffer | N/A |
| PyMemoryView | N/A | memoryview | memoryview <...> |
+--------------+-------------+------------+--------------------------+
리터럴 표기법
Python 3.0a1에 도입된 b’…’ 표기법은 어떤 변형을 사용하든 불변 바이트 객체를 반환합니다. 가변 바이트 배열을 만들려면 bytearray(b’…’) 또는 bytearray([…])를 사용하십시오. 후자의 형식은 0부터 255까지의 범위에 있는 정수 목록을 받습니다.
기능
PEP 3118 버퍼 API
bytes와 bytearray는 모두 PEP 3118 버퍼 API를 구현합니다. bytes 타입은 읽기 전용 요청만 구현하지만, bytearray 타입은 쓰기 가능한 요청과 데이터 잠금 요청도 허용합니다. 요소 데이터 타입은 항상 ‘B’(즉, 부호 없는 바이트)입니다.
생성자
bytes와 bytearray 모두에 적용되는 생성자 형식은 네 가지입니다.
bytes(<bytes>),bytes(<bytearray>),bytearray(<bytes>),bytearray(<bytearray>): 단순 복사 생성자이며,bytes(<bytes>)는 (불변인) 인자를 반환할 수도 있지만bytearray(<bytearray>)는 항상 복사본을 만듭니다.bytes(<str>, <encoding>[, <errors>]),bytearray(<str>, <encoding>[, <errors>]): 텍스트 문자열을 인코딩합니다.str.encode()메서드는 불변 bytes 객체를 반환한다는 점에 유의하십시오. <encoding> 인자는 필수이고 <errors>는 선택 사항입니다. 지정하는 경우 <encoding>과 <errors>는str인스턴스여야 합니다.bytes(<memory view>),bytearray(<memory view>): PEP 3118 버퍼 API를 구현하는 모든 대상으로부터 bytes 또는 bytearray 객체를 생성합니다.bytes(<iterable of ints>),bytearray(<iterable of ints>): 256 미만의 정수 스트림으로부터 bytes 또는 bytearray 객체를 생성합니다.bytes(<int>),bytearray(<int>): 지정된 길이의 0으로 초기화된 bytes 또는 bytearray 객체를 생성합니다.
비교
bytes 타입과 bytearray 타입은 서로 비교할 수 있고 순서 비교도 가능하므로, 예를 들어 b’abc’ == bytearray(b’abc’) < b’abd’가 성립합니다.
어느 타입이든 str 객체와 동등성을 비교하면 두 피연산자의 내용과 관계없이 False를 반환합니다. str과 순서 비교를 수행하면 TypeError를 발생시킵니다. 이는 호환되지 않는 타입의 객체 간 비교 및 순서 비교에 대한 표준 규칙을 모두 따릅니다.
(참고: Python 3.0a1에서는 bytes 인스턴스와 str 인스턴스를 비교하면 TypeError를 발생시켰습니다. 특히 Python 2.x에서 포팅된 코드에서 가끔 발생하는 실수를 더 빨리 찾아낼 수 있으리라는 전제에서였습니다. 그러나 python-3000 목록에서 이루어진 긴 논의에서는 이 방식에 너무 많은 문제가 있음이 지적되었으므로, 이 PEP의 나머지 부분이 어떻게 되든 3.0a2에서 이를 되돌리는 것이 명백히 잘못된 생각임이 드러났습니다.)
슬라이싱
bytes 객체를 슬라이싱하면 bytes 객체를 반환합니다. bytearray 객체를 슬라이싱하면 bytearray 객체를 반환합니다.
bytearray 객체에 대한 슬라이스 할당은 PEP 3118 버퍼 API를 구현하는 모든 대상 또는 256 미만의 정수 이터러블을 허용합니다.
인덱싱
bytes와 bytearray를 인덱싱하면 작은 정수를 반환합니다(3.0a1의 bytes 타입 및 lists나 array.array(‘B’)와 같습니다).
bytearray 객체의 항목에 할당할 때는 256 미만의 정수를 허용합니다. (bytes 시퀀스로 할당하려면 슬라이스 할당을 사용하십시오.)
Str() 및 Repr()
str() 함수와 repr() 함수는 이러한 객체에 대해 동일한 것을 반환합니다. bytes 객체의 repr()은 b’…’ 형식의 리터럴을 반환합니다. bytearray의 repr()은 “bytearray(b’…’)” 형식의 문자열을 반환합니다.
연산자
다음 연산자는 언급된 경우를 제외하고 bytes 타입과 bytearray 타입에 의해 구현됩니다:
b1 + b2: 연결입니다. bytes/bytearray 피연산자가 혼합된 경우, 반환 형식은 첫 번째 인자의 형식입니다 (이는+=가 작동하는 방식을 고려하기 전에는 임의적으로 보입니다).b1 += b2: b1이 bytearray 객체인 경우 b1을 변경합니다.b * n,n * b: 반복입니다. n은 정수여야 합니다.b *= n: b가 bytearray 객체인 경우 b를 변경합니다.b1 in b2,b1 not in b2: 부분 문자열 테스트입니다. b1은 PEP 3118 버퍼 API를 구현하는 모든 객체일 수 있습니다.i in b,i not in b: 단일 바이트 포함 테스트입니다. i는 정수여야 합니다(길이가 1인 바이트 배열인 경우에도 동일한 결과가 나오는 부분 문자열 테스트로 간주됩니다).len(b): 바이트 수입니다.hash(b): 해시 값입니다. bytes 형식에서만 구현됩니다.
% 연산자는 구현되지 않는다는 점에 유의하십시오. 복잡성을 고려하면 구현할 가치가 없어 보입니다.
메서드입니다.
다음 메서드는 bytes와 bytearray 모두에서 유사한 의미로 구현됩니다. bytes 인자에 대해서는 PEP 3118 버퍼 API를 구현하는 모든 것을 허용하며, 메서드가 호출된 객체(“self”)와 동일한 형식을 반환합니다.:
.capitalize(), .center(), .count(), .decode(), .endswith(),
.expandtabs(), .find(), .index(), .isalnum(), .isalpha(), .isdigit(),
.islower(), .isspace(), .istitle(), .isupper(), .join(), .ljust(),
.lower(), .lstrip(), .partition(), .replace(), .rfind(), .rindex(),
.rjust(), .rpartition(), .rsplit(), .rstrip(), .split(),
.splitlines(), .startswith(), .strip(), .swapcase(), .title(),
.translate(), .upper(), .zfill()
이는 .encode()를 제외하면 Python 2.x의 str 형식에 존재하는 메서드 집합과 정확히 같습니다. 시그니처와 의미도 동일합니다. 그러나 letter, whitespace, lower case와 같은 문자 클래스가 사용될 때는 이러한 클래스의 ASCII 정의가 사용됩니다. (Python 2.x의 str 형식은 현재 로캘의 정의를 사용하며, 이는 locale 모듈을 통해 설정할 수 있습니다.) Python 3000에서는 인코딩과 디코딩의 정의가 더 엄격하기 때문에 .encode() 메서드는 제외됩니다. 인코딩은 항상 유니코드 문자열을 받아 바이트 시퀀스를 반환하고, 디코딩은 항상 바이트 시퀀스를 받아 유니코드 문자열을 반환합니다.
또한 두 형식 모두 16진수 값이 포함된 문자열로부터 객체를 생성하는 클래스 메서드 .fromhex()를 구현합니다(바이트 사이에 공백이 있거나 없어도 됩니다).
bytearray 형식은 MutableSequence ABC에서 가져온 다음 추가 메서드를 구현합니다(PEP 3119 참조).
.extend(), .insert(), .append(), .reverse(), .pop(), .remove()입니다.
Bytes 및 Str 형식입니다.
Python 3.0a1의 bytes 형식과 마찬가지로, Python 2.x에서 str과 unicode의 관계와는 달리, 인코딩을 지정하지 않고 bytes(또는 bytearray) 객체와 str 객체를 혼합하려고 하면 TypeError 예외가 발생합니다. (그러나 bytes/bytearray 객체와 str 객체의 동일성 비교는 단순히 False를 반환합니다. 위의 비교 섹션을 참조하십시오.)
bytes 또는 bytearray 객체와 str 객체 간의 변환은 인코딩을 사용하여 항상 명시적으로 수행해야 합니다. 서로 동등한 API가 두 가지 있습니다. str(b, <encoding>[, <errors>])는 b.decode(<encoding>[, <errors>])와 동등하고, bytes(s, <encoding>[, <errors>])는 s.encode(<encoding>[, <errors>])와 동등합니다.
한 가지 예외가 있습니다. str(b)라고 작성하면 인코딩을 지정하지 않고 bytes(또는 bytearray)에서 str로 변환할 수 있습니다. 이는 repr(b)와 동일한 결과를 생성합니다. 이 예외는 모든 객체를 출력할 수 있다는 일반적인 약속 때문에 필요하며, 출력은 str로 변환하는 특수한 경우에 불과합니다. 그러나 bytes 객체를 출력할 때 개별 바이트를 문자로 해석한다는 보장은 없습니다(Python 2.x와는 다릅니다).
str 형식은 현재 PEP 3118 버퍼 API를 구현합니다. 이는 가끔 편리할 수 있지만 잠재적으로 혼란을 일으키기도 합니다. 버퍼 API를 통해 액세스하는 바이트가 플랫폼에 따라 달라지는 인코딩을 나타내기 때문입니다. 플랫폼의 바이트 순서와 컴파일 시 구성 옵션에 따라 인코딩은 UTF-16-BE, UTF-16-LE, UTF-32-BE 또는 UTF-32-LE일 수 있습니다. 더 나쁜 점은 str 형식의 다른 구현이 바이트 표현을 완전히 변경할 수 있다는 것입니다. 예를 들어 UTF-8로 변경하거나, 데이터를 연속된 바이트 배열로 전혀 액세스할 수 없게 만들 수도 있습니다. 따라서 PEP 3118 버퍼 API는 str 형식에서 제거됩니다.
basestring 타입
basestring 타입은 언어에서 제거됩니다. isinstance(x, basestring)라고 작성하던 코드는 대신 isinstance(x, str)를 사용하도록 변경해야 합니다.
피클링
독자의 연습 문제로 남겨 둡니다.
Copyright
This document has been placed in the public domain.