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

Python 개선 제안 한국어 번역

PEP 358 – “bytes” 객체

Author:
Neil Schemenauer <nas at arctrix.com>, Guido van Rossum <guido at python.org>
Status:
Final
Type:
Standards Track
Created:
15-Feb-2006
Python-Version:
2.6, 3.0
Post-History:


Table of Contents

번역·라이선스 안내

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

갱신

이 PEP는 PEP 3137에 의해 부분적으로 대체되었습니다.

초록

이 PEP는 원시 바이트 시퀀스 타입의 도입을 개괄합니다. bytes 타입을 추가하는 것은 Python 3.0에서 도입될 유니코드 기반 str 객체로 전환하는 과정의 한 단계입니다.

이 PEP는 bytes 타입이 Python 2.6에서 어떻게 동작해야 하는지와 Python 3.0에서 어떻게 동작해야 하는지를 설명합니다. (때때로 차이가 있는데, Python 2.6에는 str과 유니코드 타입인 unicode라는 두 가지 문자열 타입이 있는 반면, Python 3.0에는 문자열 타입이 하나만 있을 것이기 때문입니다. 그 타입의 이름은 str이 되지만 의미는 2.6의 유니코드 타입인 unicode와 같을 것입니다.)

동기

현재 Python의 문자열 객체는 여러 용도로 과도하게 사용되고 있습니다. 문자열 객체는 문자 시퀀스와 바이트 시퀀스를 모두 담는 데 사용됩니다. 이렇게 용도가 중복되어 혼란과 버그가 생깁니다. 향후 Python 버전에서 문자열 객체는 문자 데이터를 담는 데 사용될 것입니다. bytes 객체는 바이트 컨테이너의 역할을 맡을 것입니다. 최종적으로 유니코드 타입인 unicode는 str로 이름이 바뀌고 기존 str 타입은 제거될 것입니다.

명세

bytes 객체는 0부터 255 범위에 있는 정수의 가변 시퀀스를 저장합니다. 문자열 객체와 달리 bytes 객체를 인덱싱하면 정수가 반환됩니다. 정수가 아닌 객체를 요소에 대입하거나 요소와 비교하면 TypeError 예외가 발생합니다. 요소에 0부터 255 범위를 벗어난 값을 대입하면 ValueError 예외가 발생합니다. bytes의 .__len__() 메서드는 시퀀스에 저장된 정수의 개수(즉, 바이트 수)를 반환합니다.

bytes 객체의 생성자는 다음과 같은 시그니처를 가집니다:

bytes([initializer[, encoding]])

인자를 하나도 제공하지 않으면 요소가 0개인 bytes 객체가 생성되어 반환됩니다. 초기화 인자는 문자열(2.6에서는 str 또는 유니코드 타입인 unicode), 정수의 이터러블, 또는 단일 정수일 수 있습니다. 생성자의 의사 코드(속도가 아닌 명확한 의미를 위해 최적화됨)는 다음과 같습니다:

def bytes(initializer=0, encoding=None):
    if isinstance(initializer, int): # In 2.6, int -> (int, long)
        initializer = [0]*initializer
    elif isinstance(initializer, basestring):
        if isinstance(initializer, unicode): # In 3.0, "if True"
            if encoding is None:
                # In 3.0, raise TypeError("explicit encoding required")
                encoding = sys.getdefaultencoding()
            initializer = initializer.encode(encoding)
        initializer = [ord(c) for c in initializer]
    else:
        if encoding is not None:
            raise TypeError("no encoding allowed for this initializer")
        tmp = []
        for c in initializer:
            if not isinstance(c, int):
                raise TypeError("initializer must be iterable of ints")
            if not 0 <= c < 256:
                raise ValueError("initializer element out of range")
            tmp.append(c)
        initializer = tmp
    new = <new bytes object of length len(initializer)>
    for i, c in enumerate(initializer):
        new[i] = c
    return new

.__repr__() 메서드는 평가하면 새 bytes 객체를 생성하는, bytes 리터럴을 담은 문자열을 반환합니다:

>>> bytes([10, 20, 30])
b'\n\x14\x1e'

이 객체에는 str 객체의 .decode() 메서드와 동등한 .decode() 메서드가 있습니다. 이 객체에는 [0-9a-fA-F ] 집합에 속한 문자로 이루어진 문자열을 받아 bytes 객체를 반환하는 클래스 메서드 .fromhex()가 있습니다(binascii.unhexlify와 유사합니다). 예를 들면 다음과 같습니다:

>>> bytes.fromhex('5c5350ff')
b'\\SP\xff'
>>> bytes.fromhex('5c 53 50 ff')
b'\\SP\xff'

이 객체에는 반대 방향의 변환을 수행하는 .hex() 메서드가 있습니다(binascii.hexlify와 유사합니다):

>> bytes([92, 83, 80, 255]).hex()
'5c5350ff'

bytes 객체에는 리스트 메서드와 유사한 메서드도 있고 str 메서드와 유사한 메서드도 있습니다. 다음은 대략적인 시그니처와 함께 나열한 전체 메서드 목록입니다:

.__add__(bytes) -> bytes
.__contains__(int | bytes) -> bool
.__delitem__(int | slice) -> None
.__delslice__(int, int) -> None
.__eq__(bytes) -> bool
.__ge__(bytes) -> bool
.__getitem__(int | slice) -> int | bytes
.__getslice__(int, int) -> bytes
.__gt__(bytes) -> bool
.__iadd__(bytes) -> bytes
.__imul__(int) -> bytes
.__iter__() -> iterator
.__le__(bytes) -> bool
.__len__() -> int
.__lt__(bytes) -> bool
.__mul__(int) -> bytes
.__ne__(bytes) -> bool
.__reduce__(...) -> ...
.__reduce_ex__(...) -> ...
.__repr__() -> str
.__reversed__() -> bytes
.__rmul__(int) -> bytes
.__setitem__(int | slice, int | iterable[int]) -> None
.__setslice__(int, int, iterable[int]) -> Bote
.append(int) -> None
.count(int) -> int
.decode(str) -> str | unicode # in 3.0, only str
.endswith(bytes) -> bool
.extend(iterable[int]) -> None
.find(bytes) -> int
.index(bytes | int) -> int
.insert(int, int) -> None
.join(iterable[bytes]) -> bytes
.partition(bytes) -> (bytes, bytes, bytes)
.pop([int]) -> int
.remove(int) -> None
.replace(bytes, bytes) -> bytes
.rindex(bytes | int) -> int
.rpartition(bytes) -> (bytes, bytes, bytes)
.split(bytes) -> list[bytes]
.startswith(bytes) -> bool
.reverse() -> None
.rfind(bytes) -> int
.rindex(bytes | int) -> int
.rsplit(bytes) -> list[bytes]
.translate(bytes, [bytes]) -> bytes

.isupper(), .upper() 등의 메서드가 눈에 띄게 빠져 있다는 점에 유의하십시오. (단, 아래의 “미해결 문제”를 참조하십시오.) 이 객체는 가변 객체이므로 .__hash__()는 없습니다. .sort() 메서드가 필요한 사용 사례는 없습니다.

bytes 타입은 버퍼 인터페이스도 지원하므로 바이너리 데이터를 읽고 쓸 수 있습니다(문자 데이터는 지원하지 않습니다).

범위 밖의 문제

  • Python 3k의 I/O 서브시스템은 지금과 크게 달라질 것입니다. 그 I/O 서브시스템이 어떻게 작동하고 bytes 객체와 어떻게 상호 작용할지 결정하는 일은 이 PEP의 범위를 벗어납니다. 다만 바이너리 I/O는 바이트를 읽고 쓰고, 텍스트 I/O는 문자열을 읽을 것으로 예상됩니다. bytes 타입은 버퍼 인터페이스를 지원하므로 Python 2.6에 있는 기존 바이너리 I/O 연산도 bytes 객체를 지원할 것입니다.
  • 객체를 바이트 배열로 변환할 수 있도록 .__bytes__()라는 특수 메서드를 언어에 추가하자는 제안이 있었습니다. 이 결정은 이 PEP의 범위를 벗어납니다.
  • b"..." 형태의 bytes 리터럴도 제안되었습니다. 이는 PEP 3112에서 다룹니다.

미해결 문제

  • .decode() 메서드는 불필요합니다. bytes 객체 b는 unicode(b, <encoding>) (2.6) 또는 str(b, <encoding>) (3.0)을 호출해서도 디코딩할 수 있기 때문입니다. encode/decode 메서드가 과연 필요합니까? 어떤 의미에서는 생성자를 사용하는 표기가 더 깔끔합니다.
  • 메서드를 더 신중하게 명세해야 합니다.
  • 피클링과 마샬링 지원을 명세해야 합니다.
  • 그 모든 리스트 메서드를 정말로 구현해야 합니까?
  • 두 번째 인자를 필수로 하여 .ljust(), .rjust(), .center()를 지원하자는 주장도 가능합니다.
  • 인자를 필수로 하여 .split()을 지원하자는 주장도 가능합니다.
  • 문자, 숫자, 공백에 대한 ASCII 정의를 사용하여 .islower(), .isupper(), .isspace(), .isalpha(), .isalnum(), .isdigit() 및 이에 대응하는 변환(.lower() 등)을 지원하자는 주장까지도 가능합니다. 이것이 받아들여진다면 .ljust(), .rjust(), .center(), .split()을 지원할 근거는 훨씬 강해지며, 이들 메서드에도 ASCII 공백 하나 또는 모든 ASCII 공백 문자(.split()의 경우)를 사용하는 기본 인자가 있어야 합니다.

자주 묻는 질문

Q: 유니코드 객체의 encode 메서드가 같은 일을 하는데, 왜 선택적 인코딩(encoding) 인자를 두는 것입니까?

A: 현재 버전의 Python에서 encode 메서드는 str 객체를 반환하며, 기존 코드를 깨뜨리지 않고는 이를 바꿀 수 없습니다. bytes(s.encode(...)) 구문은 바이트 시퀀스를 여러 번 복사해야 하므로 비용이 큽니다. 또한 Python은 일반적으로 타입 A의 객체를 타입 B의 객체로 변환하는 두 가지 방법을 제공합니다. A 인스턴스에게 자신을 B로 변환하도록 요청하거나, 타입 B에게 A로부터 새 인스턴스를 생성하도록 요청하는 것입니다. A와 B가 무엇이냐에 따라 두 API 모두 타당합니다. 때로는 결합도를 낮추기 위해 A가 B를 알 수 없어야 하며, 이 경우에는 후자의 방법을 사용해야 합니다. 때로는 B가 A를 알 수 없으며, 이 경우에는 전자의 방법을 사용해야 합니다.

Q: 초기화 값이 str일 때 bytes는 왜 인코딩(encoding) 인자를 무시합니까? (이는 2.6에만 해당합니다.)

A: 그 경우에는 인코딩이 가질 수 있는 합리적인 의미가 없습니다. str 객체는 실제로 바이트 배열이며, 자신이 담고 있는 문자 데이터의 인코딩에 대해 아무것도 알지 못합니다. 프로그래머가 원하는 인코딩을 이미 사용하는 str 객체를 제공했다고 가정해야 합니다. 바이트를 그대로 복사하는 것 이외의 동작이 필요하다면 먼저 문자열을 디코딩해야 합니다. 예를 들면 다음과 같습니다.:

bytes(s.decode(encoding1), encoding2)

Q: 인코딩 인자의 기본값을 ASCII 대신 Latin-1(또는 전체 바이트 범위를 포괄하는 다른 인코딩)로 하지 않는 이유는 무엇입니까?

A: Python의 시스템 기본 인코딩은 ASCII입니다. 그 기본값을 사용하는 것이 가장 덜 혼란스러워 보입니다. 또한 Py3k에서는 Latin-1을 기본값으로 사용하는 것이 사용자의 기대와 다를 수 있습니다. 예를 들어 사용자는 유니코드 인코딩을 선호할 수도 있습니다. 어떤 기본값이든 항상 기대한 대로 동작하지는 않습니다. 적어도 ASCII는 ASCII가 아닌 데이터를 인코딩하려고 하면 요란하게 오류를 알립니다.