我有期望的代码,str但可以通过bytes以下方式处理被传递的情况:
if isinstance(data, bytes):
data = data.decode()
不幸的是,这不适用于bytearray。有没有更通用的方法来测试对象是否为bytes或bytearray,或者我应该只检查两者?是否hasattr('decode')像我想的那样糟糕?
我有期望的代码,str但可以通过bytes以下方式处理被传递的情况:
if isinstance(data, bytes):
data = data.decode()
不幸的是,这不适用于bytearray。有没有更通用的方法来测试对象是否为bytes或bytearray,或者我应该只检查两者?是否hasattr('decode')像我想的那样糟糕?
str。其他一些代码应尽快在输入时从字节转换为Unicode。(2)“类似字节的字节”在Python中具有特殊含义(支持缓冲区协议的对象(仅C))
Answers:
您可以在此处使用几种方法。
由于Python是鸭子类型的,因此您可以简单地执行以下操作(这似乎通常是建议的方式):
try:
data = data.decode()
except (UnicodeDecodeError, AttributeError):
pass
您可以hasattr按照您的描述使用,但是可能会很好。当然,这是假定.decode()给定对象的方法返回一个字符串,并且没有讨厌的副作用。
我个人建议使用异常或hasattr方法,但是您可以使用任何方法。
这种方法并不常见,但可以:
data = str(data, "utf-8")
就像缓冲区协议的一样,其他编码也是允许的.decode()。您还可以传递第三个参数来指定错误处理。
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。
hasattr尝试/例外,以防止您误吞一些解码功能中的错误,但+1。
您可以使用:
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'>
除非您知道我们不了解的内容,否则此代码是不正确的:
if isinstance(data, bytes):
data = data.decode()
您(似乎)不知道的编码data。您假设它是UTF-8,但这很可能是错误的。由于您不知道编码,因此没有text。您有字节,在阳光下可能有任何意义。
好消息是,大多数随机字节序列都不是有效的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必须是第一个参数。如果这些限制对您不起作用,请改用其他答案之一。
这取决于您要解决的问题。如果要使用相同的代码将两种情况都转换为字符串,则只需将类型转换为bytesfirst,然后解码即可。这样,它是单线的:
#!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中对象是否为类似字节的对象的正确方法是什么?这包括在内置类型的若干(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()。
.release(),或使用上下文管理器版本。
memoryview将立即释放并被.release()隐式调用。但是我同意最好不要依赖于此,因为并非所有Python实现都是引用计数的。
测试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>")
这有点丑陋,但是它可以完成问题所要求的工作,并且始终以最简单的方式起作用。
str对象是 bytes:str is bytes->True在Python2中