Подтвердить что ты не робот

Каков правильный способ определить, является ли объект байтовым объектом в Python?

У меня есть код, который ожидает str, но будет обрабатывать случай передачи bytes следующим образом:

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

К сожалению, это не работает в случае bytearray. Существует ли более общий способ проверки того, является ли объект либо bytes, либо bytearray, или я должен просто проверить для обоих? Является ли hasattr('decode') настолько плохим, насколько я себя чувствую?

4b9b3361

Ответ 1

Здесь вы можете использовать несколько подходов.

Утка печатает

Так как Python набран уткой, вы можете просто сделать следующее (что, как обычно и предлагается):

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
    ...

Вы также можете создавать специальные обработчики для объектов bytearray и bytes, если хотите.

Осторожно,: функции с одной отправкой работают только с первым аргументом! Это преднамеренная функция, см. PEP 433.

Ответ 2

Вы можете использовать:

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

К сожалению, под python 3.4 они одинаковы....

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

Ответ 3

Этот код неверен, если вы не знаете, чего мы не знаем:

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 должны быть первым аргументом. Если эти ограничения не работают для вас, используйте один из других ответов.

Ответ 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' явно в декодирование, если вам не нужно оставлять несколько байтов. Причина в том, что в следующий раз, когда вы или кто-то еще прочитаете исходный код, ситуация будет более очевидной.

Ответ 5

Здесь есть два вопроса, и ответы на них разные.

Первый вопрос, заголовок этого сообщения: Каков правильный способ определить, является ли объект байтовым объектом в Python? Это включает в себя ряд встроенных типов (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 'выше ответ на этот вопрос велик. Обратите внимание, что не все байтоподобные объекты поддерживают декодирование().

Ответ 6

не уверен, почему никто не предложил следующее, которое ИМХО так чисто:

if type(data) == bytes:
    data = data.decode()