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

A. *_*cox 58 python python-3.x

我有预期的代码,str但将处理bytes以下列方式传递的情况:

if isinstance(data, bytes):
    data = data.decode()
Run Code Online (Sandbox Code Playgroud)

不幸的是,这不适用于bytearray.有没有测试对象是否可以是一个更通用的方法bytesbytearray,或者我应该检查这两个?是否hasattr('decode')像我觉得的那样糟糕?

小智 55

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

鸭打字

由于Python是鸭子类型,你可以简单地做如下(这似乎是通常建议的方式):

try:
    data = data.decode()
except (UnicodeDecodeError, AttributeError):
    pass
Run Code Online (Sandbox Code Playgroud)

hasattr但是,你可以按照你的描述使用它,它可能会很好.当然,这是假设.decode()给定对象的方法返回一个字符串,并且没有令人讨厌的副作用.

我个人推荐例外或hasattr方法,但无论你使用什么,都取决于你.

使用str()

这种方法并不常见,但有可能:

data = str(data, "utf-8")
Run Code Online (Sandbox Code Playgroud)

其他编码是允许的,就像缓冲协议一样.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
    ...
Run Code Online (Sandbox Code Playgroud)

如果您愿意,也可以为对象bytearraybytes对象创建特殊处理程序.

注意:单调度函数仅适用于第一个参数!这是一个有意的特征,见PEP 433.

  • 由于在 str 上调用 str 没有任何作用,而且在我看来是最清楚的,所以我同意了。 (3认同)
  • 这里确实应该提到collections.abc.ByteString (2认同)

zan*_*ngw 27

您可以使用:

isinstance(data, (bytes, bytearray))
Run Code Online (Sandbox Code Playgroud)

由于这里使用了不同的基类.

>>> bytes.__base__
<type 'basestring'>
>>> bytearray.__base__
<type 'object'>
Run Code Online (Sandbox Code Playgroud)

去检查 bytes

>>> by = bytes()
>>> isinstance(by, basestring)
True
Run Code Online (Sandbox Code Playgroud)

然而,

>>> buf = bytearray()
>>> isinstance(buf, basestring)
False
Run Code Online (Sandbox Code Playgroud)

上面的代码是在python 2.7下测试的

不幸的是,在python 3.4下,它们是相同的....

>>> bytes.__base__
<class 'object'>
>>> bytearray.__base__
<class 'object'>
Run Code Online (Sandbox Code Playgroud)


Jee*_*eva 16

>>> content = b"hello"
>>> text = "hello"
>>> type(content)
<class 'bytes'>
>>> type(text)
<class 'str'>
>>> type(text) is str
True
>>> type(content) is bytes
True
Run Code Online (Sandbox Code Playgroud)


Kev*_*vin 10

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

if isinstance(data, bytes):
    data = data.decode()
Run Code Online (Sandbox Code Playgroud)

你没有(似乎)知道编码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, ...)
Run Code Online (Sandbox Code Playgroud)

这不适用于方法,data必须是第一个参数.如果这些限制不适合您,请使用其他答案之一.