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

时间:2016-01-19 06:28:10

标签: python python-3.x

我的代码需要str,但会处理以下列方式传递bytes的情况:

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

不幸的是,这在bytearray的情况下不起作用。是否有更通用的方法来测试对象是bytes还是bytearray,还是应该检查两者? hasattr('decode')和我一样糟糕吗?

7 个答案:

答案 0 :(得分:42)

您可以在这里使用一些方法。

鸭子打字

由于Python是duck typed,您可以按照以下方式执行操作(这似乎是通常建议的方式):

try:
    data = data.decode()
except 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 :(得分:21)

您可以使用:

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

答案 2 :(得分:8)

除非您知道我们不知道的事情,否则此代码不正确:

<?php
    \app\assets\TransportAsset::register($this);
?>
<!-- Some HTML Code -->

您(似乎)不知道if isinstance(data, bytes): data = data.decode() 的编码。你假设it's UTF-8,但这很可能是错的。由于您不知道编码,you do not have text。你有字节,在阳光下可能有任何意义。

好消息是大多数随机字节序列都不是有效的UTF-8,所以当它中断时,它会大声破坏(data是默认值),而不是默默地做错事。更好的消息是,大多数恰好是有效UTF-8的随机序列也是有效的ASCII,(nearly)每个人都同意如何解析。

坏消息是没有合理的方法来解决这个问题。有一种提供编码信息的标准方法:使用errors='strict'代替str。如果某些第三方代码向您发送了bytesbytes对象而没有任何进一步的上下文或信息,那么唯一正确的操作就是失败。

现在,假设您确实知道编码,可以在此处使用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, ...) 必须是第一个参数。如果这些限制不适合您,请改用其他答案之一。

答案 3 :(得分:3)

这取决于你想要解决的问题。如果您希望使用相同的代码将两种情况转换为字符串,则可以先将类型转换为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'写入解码,如果你不关心几个字节。原因是下次您或其他人阅读源代码时,情况会更明显。

答案 4 :(得分:1)

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

第一个问题,这篇文章的标题是确定一个对象是否是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()。

答案 5 :(得分:1)

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

答案 6 :(得分:0)

测试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>")

这有点难看,但它确实完成了问题提出的任务,并且始终以最简单的方式起作用。