在 Python 中,确定对象是否是类字节对象的正确方法是什么?

我有代码,期望 str,但将处理的情况下,通过 bytes以下方式:

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

不幸的是,这在 bytearray的情况下不起作用。有没有一种更通用的方法来测试一个对象是 bytes还是 bytearray,或者我应该同时检查两者?hasattr('decode')有我想的那么糟糕吗?

113061 次浏览

你可使用:

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

这里有几种方法可以使用。

鸭子打字

由于 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及以上版本包括一个通过 函数工具实现的称为单分派通用函数的漂亮特性。这句话有点冗长,但也更明确:

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

这个代码是不正确的,除非你知道一些我们不知道的东西:

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

你似乎不知道 data的编码。您假设的是 是 UTF-8,但这很可能是错误的。因为你不知道编码,你没有短信。你有字节,在太阳底下可以有任何意义。

好消息是,大多数随机字节序列都不是有效的 UTF-8,所以当它中断时,它将大声中断(默认为 errors='strict') ,而不是默默地做错事情。更好的消息是,大多数正好是有效的 UTF-8的随机序列也是有效的 ASCII (差不多) ,无论如何大家都同意如何解析它。

坏消息是,没有合理的方法来解决这个问题。提供编码信息的标准方法是: 使用 str而不是 bytes。如果某些第三方代码在没有任何进一步上下文或信息的情况下提供了 bytesbytearray对象,那么唯一正确的操作就是失败。


现在,假设您知道编码,您可以在这里使用 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必须是第一个参数。如果这些限制对你不起作用,用其他的答案代替。

这取决于你想解决什么问题。如果希望使用将两种情况转换为字符串的相同代码,只需将类型转换为 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'明确的解码,如果你不介意节省几个字节。原因是,下次您或其他人阅读源代码时,情况会更加明显。

这里有两个问题,答案是不同的。

第一个问题,这篇文章的标题,是 在 Python 中,确定对象是否是类字节对象的正确方法是什么?这包括一些内置的类型(bytesbytearrayarray.arraymemoryview,其他?)也可能是用户定义的类型。据我所知,检查这些问题的最好方法是尝试用它们创建一个 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 ()。

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

测试 if isinstance(data, bytes)if type(data) == bytes等等在 Python2中不起作用,其中一个简单的 ASCII 字符串通过了!因为我同时使用 Python2和 Python3,为了克服这个问题,我做了以下检查:

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

它有点丑陋,但它的工作 问题是和它总是工作,在最简单的方式。


经过很长一段时间... ... 这个词更短、更简单,也更“优雅”:

if not type(data) == str: print("It's bytes")