파이썬에서 객체가 바이트와 같은 객체인지 결정하는 적절한 방법은 무엇입니까?


90

예상 str하지만 bytes다음과 같은 방식 으로 전달되는 경우를 처리하는 코드 가 있습니다 .

if isinstance(data, bytes):
    data = data.decode()

안타깝게도 bytearray. 객체가 bytes또는 인지 여부를 테스트하는보다 일반적인 방법이 bytearray있습니까? 아니면 둘 다 확인해야합니까? 가 hasattr('decode')내가 될 것이라고 느낌으로 나쁜로?


6
개인적으로 나는 다음 사람만큼 파이썬의 오리 타이핑을 좋아합니다. 그러나 입력 인수를 확인하고 다른 유형으로 강제해야하는 경우 더 이상 덕 타이핑이 아닙니다. 코드를 유지 관리하기 더 어렵게 만드는 것입니다. 여기에서 내 제안은 (그리고 다른 사람들은 동의하지 않을 수 있음) 여러 함수를 만드는 것입니다 (유형 강제를 처리하고 기본 구현에 위임).
mgilson

(1) 레거시 Python 2 코드와의 호환성을 위해 필요하지 않는 한; 텍스트와 이진 데이터를 동시에 받아들이지 마십시오. 함수가 텍스트와 함께 작동하면 str. 일부 다른 코드는 입력시 가능한 한 빨리 바이트에서 유니 코드로 변환해야합니다. (2) "bytes-like"는 Python에서 특별한 의미를 갖습니다 (버퍼 프로토콜을 지원하는 객체 (C 만 해당))
jfs

주요 문제는이 함수가 Python 2에서 작동하지 않는다는 것입니다. 간단한 ASCII 문자열이 <bytes> 테스트를 통과합니다!
Apostolos

답변:


73

여기에서 사용할 수있는 몇 가지 접근 방식이 있습니다.

오리 타이핑

Python은 duck typed 이므로 다음과 같이 간단하게 수행 할 수 있습니다 (일반적으로 제안되는 방식 인 것 같습니다).

try:
    data = data.decode()
except (UnicodeDecodeError, AttributeError):
    pass

hasattr그러나 설명대로 사용할 수 있으며 아마도 괜찮을 것입니다. 물론 이것은 .decode()주어진 객체에 대한 메서드가 문자열을 반환하고 불쾌한 부작용이 없다고 가정합니다 .

개인적으로 예외 나 hasattr방법을 권장 하지만 사용하는 것은 귀하에게 달려 있습니다.

str () 사용

이 방법은 흔하지 않지만 가능합니다.

data = str(data, "utf-8")

다른 인코딩은 버퍼 프로토콜의 .decode(). 세 번째 매개 변수를 전달하여 오류 처리를 지정할 수도 있습니다.

단일 배포 일반 함수 (Python 3.4 이상)

Python 3.4 이상에는 functools.singledispatch 를 통해 단일 배포 제네릭 함수라는 멋진 기능이 포함되어 있습니다. 이것은 좀 더 장황하지만 더 분명합니다.

def func(data):
    # This is the generic implementation
    data = data.decode()
    ...

@func.register(str)
def _(data):
    # data will already be a string
    ...

원하는 경우 bytearraybytes개체에 대한 특수 처리기를 만들 수도 있습니다 .

주의 : 단일 디스패치 함수는 첫 번째 인수에서만 작동합니다! 이것은 의도적 인 기능 입니다. PEP 433을 참조하십시오 .


제공되는 표준 라이브러리를 완전히 잊어 버린 단일 디스패치 제네릭에 대한 언급은 +1입니다.
A. Wilcox

str에서 str을 호출하는 것은 아무 일도하지 않고 나에게 가장 분명해 보였기 때문에 나는 그것을 사용했습니다.
A. Wilcox

전반적으로 나는 hasattr디코딩 기능에서 실수로 버그를 삼키는 것을 방지하기 위해 try / except보다 더 좋아 하지만 +1.
keredson

37

당신이 사용할 수있는:

isinstance(data, (bytes, bytearray))

다른 기본 클래스로 인해 여기에 사용됩니다.

>>> bytes.__base__
<type 'basestring'>
>>> bytearray.__base__
<type 'object'>

확인하다 bytes

>>> by = bytes()
>>> isinstance(by, basestring)
True

하나,

>>> buf = bytearray()
>>> isinstance(buf, basestring)
False

위 코드는 python 2.7에서 테스트되었습니다.

불행히도 파이썬 3.4에서는 동일합니다 ....

>>> bytes.__base__
<class 'object'>
>>> bytearray.__base__
<class 'object'>

1
six.string_types는 2/3와 호환되어야합니다.
Joshua Olson

이런 종류의 검사는 간단한 ASCII 문자열이 <bytes> 테스트를 통과하는 Python 2에서 작동하지 않습니다!
Apostolos

12
>>> content = b"hello"
>>> text = "hello"
>>> type(content)
<class 'bytes'>
>>> type(text)
<class 'str'>
>>> type(text) is str
True
>>> type(content) is bytes
True

이것은 문자열 객체가 바이트로도 전달되는 Python 2 에서 신뢰할 수있는 테스트가 아닙니다 ! 즉, 위의 코드를 기반 type(text) is bytes으로 True가됩니다!
Apostolos

11

이 코드는 우리가 모르는 것을 알지 않는 한 정확하지 않습니다.

if isinstance(data, bytes):
    data = data.decode()

의 인코딩을 모르는 것처럼 보입니다 data. 당신은 그것이 UTF-8 이라고 가정 하고 있지만 그것은 매우 잘못되었을 수 있습니다. 인코딩을 모르기 때문에 텍스트가 없습니다 . 태양 아래에서 어떤 의미를 가질 수있는 바이트가 있습니다.

좋은 소식은 대부분의 임의의 바이트 시퀀스가 ​​유효한 UTF-8이 아니므로 이것이 중단되면 errors='strict'조용히 잘못된 작업을 수행하는 대신 크게 중단됩니다 ( 기본값). 짝수 더 좋은 소식은 유효한 UTF-8로 발생하는 임의 시퀀스의 대부분은 또한 (유효한 ASCII, 있다는 것입니다 거의 ) 모든 사람이 어쨌든 구문 분석하는 방법에 동의합니다.

나쁜 소식은이 문제를 해결할 합리적인 방법이 없다는 것입니다. 인코딩 정보를 제공하는 표준 방법이 있습니다. str대신 bytes. 일부 타사 코드가 추가 컨텍스트 또는 정보없이 bytes또는 bytearray객체를 전달한 경우 유일한 올바른 조치는 실패입니다.


이제 인코딩을 알고 있다고 가정하면 functools.singledispatch여기에서 사용할 수 있습니다.

@functools.singledispatch
def foo(data, other_arguments, ...):
    raise TypeError('Unknown type: '+repr(type(data)))

@foo.register(str)
def _(data, other_arguments, ...):
    # data is a str

@foo.register(bytes)
@foo.register(bytearray)
def _(data, other_arguments, ...):
    data = data.decode('encoding')
    # explicit is better than implicit; don't leave the encoding out for UTF-8
    return foo(data, other_arguments, ...)

이것은 메서드에서 작동하지 않으며 data첫 번째 인수 여야합니다. 이러한 제한 사항이 효과가 없으면 대신 다른 답변 중 하나를 사용하십시오.


내가 쓰고있는 라이브러리에서이 특정 방법에 대해 내가받는 바이트 및 / 또는 바이트 배열이 UTF-8로 인코딩되었음을 확실히 알고 있습니다.
A. Wilcox

1
@AndrewWilcox : 충분하지만 향후 Google 트래픽을 위해이 정보를 남겨 두겠습니다.
Kevin

4

해결하려는 내용에 따라 다릅니다. 두 경우를 모두 문자열로 변환하는 동일한 코드를 사용하려면 bytes먼저 유형을 먼저 변환 한 다음 디코딩하면됩니다. 이렇게하면 한 줄짜리가됩니다.

#!python3

b1 = b'123456'
b2 = bytearray(b'123456')

print(type(b1))
print(type(b2))

s1 = bytes(b1).decode('utf-8')
s2 = bytes(b2).decode('utf-8')

print(s1)
print(s2)

이런 식으로 답은 다음과 같습니다.

data = bytes(data).decode()

어쨌든 'utf-8'몇 바이트를 절약하지 않으려면 디코딩에 명시 적 으로 작성하는 것이 좋습니다 . 그 이유는 다음에 당신이나 다른 사람이 소스 코드를 읽을 때 상황이 더 분명해질 것이기 때문입니다.


3

여기에는 두 가지 질문이 있으며 그에 대한 답변은 다릅니다.

첫 번째 질문 인이 게시물의 제목은 파이썬에서 객체가 바이트와 같은 객체인지 결정하는 적절한 방법무엇입니까?입니다. 이 유형의 내장의 숫자를 포함 ( bytes, bytearray, array.array, memoryview, 등?) 가능성이 또한 사용자 정의 형식. 내가 아는 가장 좋은 방법은 이것들을 확인하는 memoryview것입니다.

>>> memoryview(b"foo")
<memory at 0x7f7c43a70888>
>>> memoryview(u"foo")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: memoryview: a bytes-like object is required, not 'str'

그러나 원본 게시물의 본문에서는 객체가 decode ()를 지원하는지 여부어떻게 테스트합니까? 라는 질문처럼 들립니다 . 이 질문에 대한 @ elizabeth-myers의 위 답변은 훌륭합니다. 모든 바이트 열류 객체가 decode ()를 지원하는 것은 아닙니다.


1
이렇게하면을 호출 .release()하거나 컨텍스트 관리자 버전을 사용해야합니다.
o11c

CPython에서 임시 memoryview가 즉시 해제되고 .release()암시 적으로 호출 될 것이라고 생각합니다. 하지만 모든 파이썬 구현이 참조 카운트되는 것은 아니기 때문에 그것에 의존하지 않는 것이 최선이라는 데 동의합니다.
잭 오코너

0

test if isinstance(data, bytes)또는 if type(data) == bytes등은 Python 2에서 작동하지 않습니다. 간단한 ASCII 문자열이! Python 2와 Python 3을 모두 사용하기 때문에이를 극복하기 위해 다음 검사를 수행합니다.

if str(type(data)).find("bytes") != -1: print("It's <bytes>")

약간 못 생겼지 만 질문이 요구 하는 작업을 수행 하고 항상 가장 간단한 방법으로 작동합니다.


Python2 str객체 bytes 다음과 같습니다 str is bytes.-> TruePython2
snakecharmerb

분명히, 따라서 탐지 문제! :)
Apostolos
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.