为啥 base64.b64encode() 返回一个字节对象?

Posted

技术标签:

【中文标题】为啥 base64.b64encode() 返回一个字节对象?【英文标题】:Why does base64.b64encode() return a bytes object?为什么 base64.b64encode() 返回一个字节对象? 【发布时间】:2017-08-04 01:04:36 【问题描述】:

base64.b64encode() 的目的是将二进制数据转换为 ASCII 安全的“文本”。但是,该方法返回一个字节类型的对象:

>>> import base64
>>> base64.b64encode(b'abc')
b'YWJj'

简单地获取该输出和decode() 很容易,但我的问题是:base64.b64encode() 返回bytes 而不是str 有什么意义?

【问题讨论】:

【参考方案1】:

base64.b64encode() 函数的目的是将二进制数据转换为 ASCII 安全的“文本”

Python 不同意这一点——base64 被故意归类为binary transform。

强制分离字节和文本并禁止隐式转换是 Python 3 的设计决定。 Python 现在对此非常严格,以至于bytes.encode 甚至不存在,因此b'abc'.encode('base64') 会引发AttributeError

该语言的观点是字节串对象是已经编码的。将字节编码为文本的编解码器不适合这种范式,因为当您想从字节域转到文本域时,它是一个解码。请注意,rot13 编码也出于同样的原因从standard encodings 列表中删除 - 它不适合 Python 3 范例。

还可以提出一个性能论点:假设 Python 自动处理 base64 输出的解码,这是由 binascii 模块中的 C 代码生成的 ASCII 编码二进制表示文本域中的 Python 对象。如果你真的想要字节,你只需要通过再次编码为 ASCII 来撤销解码。这将是一次浪费的往返,一次不必要的双重否定。最好“选择加入”解码到文本的步骤。

【讨论】:

我认为“将字节编码为文本的编解码器不适合此范例,因为当您想从字节域转到文本域时,它就是解码”为我解释了这一点。因此,单独来看它可能并不完全有意义,但本着使所有 encode()/decode() 方法具有统一的输入/输出的精神,它是有意义的。我还是觉得有点奇怪:) 我会说 99.99% 的时间你都希望它作为一个字符串,这应该是默认值。如果您关心性能或其他细微差别,您可以调用另一个函数。 从某种意义上说,base64 编码是纯文本的,只有 ASCII根据定义,这意味着它的目的是 将二进制数据转换为文本表示 .我看不出 Python 实现产生字节的任何原因。字节和文本的分离本身就非常有用,但是在这种情况下,我个人认为如果在这种情况下代码不适合这种范式,那么根本不应该应用这种范式。【参考方案2】:

b64encode() 不可能知道你想用它的输出做什么。

虽然在许多情况下您可能希望将编码值视为文本,但在许多其他情况下(例如,通过网络发送)您可能希望将其视为字节。

由于b64encode()不知道,所以拒绝猜测。而且由于输入是bytes,所以输出保持相同的类型,而不是被隐式强制转换为str

正如您所指出的,将输出解码为 str 很简单:

base64.b64encode(b'abc').decode('ascii')

...以及对结果的明确说明。

顺便说一句,值得注意的是虽然base64.b64decode()(注意:de代码,而不是zhcode) 从 3.3 版开始接受 str,更改为 somewhat controversial。

【讨论】:

感谢您的回答,虽然我对这个解释有点问题,但潜在的输出总是可以用 ascii 字符串表示,从某种意义上说,它是字节对象的子集。如果可能的话,我认为您应该以更窄的类型返回结果,字节对象可以是任何东西。通常,如果您有一个函数,您将不知道输出做了什么,您仍然希望以有意义的描述性方式返回它,否则所有函数都应该只返回字节,我们应该取消 str 类型。 也就是说b64encode()一直都知道输出可以表示为str,那为什么不返回str呢? 请注意,“为什么不返回一个str呢?”之间没有区别。和“为什么不返回一个字节对象呢?” ...它必须选择something,并且bytes被认为最符合应避免隐式强制的原则。 另请注意,str绝对不是bytes 的子集或更窄:前者最多包含 1,114,112 个不同的代码点,而后者只能表示 256 种不同的状态(可能是整数、字符或其他)。 ASCII 恰好可以在两者的子集中表示,base64 字母表也是如此,但没有内在的理由假设一个比另一个更自然。 @Code-Apprentice 我的思路是“如果你有关于返回数据的附加信息,那么就提供它”以及该方法的输出将始终在 ascii 安全范围内的事实作为那种信息。选择称它为“狭窄”可能是一个糟糕的选择。否则,我们总是可以为所有内容返回字节对象,因为所有数据都可以表示为原始字节——但这可能不是很有用。

以上是关于为啥 base64.b64encode() 返回一个字节对象?的主要内容,如果未能解决你的问题,请参考以下文章

python使用base64的加密与解密

用python实现base64编码与解码

base64模块 简单了解

base64 加密

day8_base64模块

python3与python2编码导致 hmac.new/base64.b64encode('value') python3各种报错