【问题标题】:How do I achieve sprintf-style formatting for bytes objects in python 3?如何在 python 3 中实现字节对象的 sprintf 样式格式?
【发布时间】:2017-07-29 04:00:55
【问题描述】:

我想在 python3 上使用原始字节对象执行 sprintf,而无需为 %s 进行任何手动转换。因此,将字节对象作为“模板”,加上任意数量的任何类型的对象并返回渲染的字节对象。这就是 python 2 的 sprintf % 运算符一直以来的工作方式。

b'test %s %s %s' % (5, b'blah','strblah') # python3 ==> error
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: %b requires bytes, or an object that implements __bytes__, not 'int'

def to_bytes(arg):
    if hasattr(arg,'encode'): return arg.encode()
    if hasattr(arg,'decode'): return arg
    return repr(arg).encode()

def render_bytes_template(btemplate : bytes, *args):
    return btemplate % tuple(map(to_bytes,args))

render_bytes_template(b'this is how we have to write raw strings with unknown-typed arguments? %s %s %s',5,b'blah','strblah')

# output: b'this is how we have to render raw string templates with unknown-typed arguments? 5 blah strblah'

但在 python 2 中,它只是内置的:

'example that just works %s %s %s' % (5,b'blah',u'strblah')
# output: 'example that just works 5 blah strblah'

有没有办法在 python 3 中做到这一点,但仍能达到与 python 2 相同的性能?请告诉我我错过了什么。这里的后备是在 cython 中实现(或者是否有 Python 3 的库可以帮助实现这一点?)但除了字符串对象的隐式编码之外,仍然没有看到为什么它从标准库中删除。我们不能只添加一个像 format_any() 这样的字节方法吗?

顺便说一句,这可没这么简单:

def render_bytes_template(btemplate : bytes, *args):
    return (btemplate.decode() % args).encode()

我不仅不想做任何不必要的编码/解码,而且字节参数是repr'd而不是被原始注入。

【问题讨论】:

  • 请注意,Python 3 现在可以保护您免受错误以及隐藏在 Python 2 中的水线之下的错误。例如,尝试打开 'unicode: %s' % (u'Ünîcódæ',) 以获取大小。

标签: python python-3.x templates incompatibility


【解决方案1】:

我想在 python3 上使用原始字节对象执行 sprintf,而无需为 %s 进行任何手动转换。

为此,所有格式参数也需要已经是bytes

自从 Py2 允许将 unicode 字符串格式化为字节字符串后,这种情况发生了变化,因为一旦引入带有 unicode 字符的 unicode 字符串,Py2 实现就容易出错。

例如,在 Python 2 上:

In [1]: '%s' % (u'é',)
Out[1]: u'\xe9'

从技术上讲,这是正确的,但不是开发人员的意图。它也不考虑使用的任何编码。

在 Python 3 OTOH 中:

In [2]: '%s' % ('é',)
Out[2]: 'é'

对于格式化字节字符串,使用字节字符串参数(仅限 Py3.5+)

b'%s %s' % (b'blah', 'strblah'.encode('utf-8'))

整数等其他类型也需要转换为字节字符串。

【讨论】:

  • 感谢您在问题中重新执行我的观察。不过也有一些出入。首先 print() 可以接受一个字节对象、一个 int 对象以及一个 unicode。所以有人可能会争辩说它根本不明确。此外,常规的 unicode 字符串允许 %s 使用 repr 处理任何事情,这也是不明确的。所以他们只去了一半。它只会增加混乱和减少功能,但这只是我的观点,显然事情不会改变。我将开始尝试不降低 python2 性能或只是远程调用 python2 的解决方法。
  • 显而易见,print 用于打印。打印编码的 unicode 字符串和 unicode 字符串本身会导致不同的输出。那是明确的。从技术上讲,在这两种情况下,对象的__repr____str__ 都用于打印目的。 “常规 unicode 字符串”可与任何其他 unicode 字符串一起使用,这在 Py3 中是默认设置。所以 repr 字符串是 unicode,__str__ 和任何未明确设置为字节字符串的字符串也是如此。这是 Python 核心开发团队的决定,必须习惯它。
【解决方案2】:

这样的东西对你有用吗?您只需要确保在开始某个 bytes 对象时将其包装在新的 B 类似字节的对象中,该对象重载了 %%= 运算符:

class B(bytes):
    def __init__(self, template):
        self._template = template

    @staticmethod
    def to_bytes(arg):
        if hasattr(arg,'encode'): return arg.encode()
        if hasattr(arg,'decode'): return arg
        return repr(arg).encode()

    def __mod__(self, other):
        if hasattr(other, '__iter__') and not isinstance(other, str):
            ret = self._template % tuple(map(self.to_bytes, other))
        else: 
            ret = self._template % self.to_bytes(other)
        return ret

    def __imod__(self, other):
        return self.__mod__(other)

a = B(b'this %s good')
b = B(b'this %s %s good string')
print(a % 'is')
print(b % ('is', 'a'))

a = B(b'this %s good')
a %= 'is'
b = B(b'this %s %s good string')
b %= ('is', 'a')
print(a)
print(b)

这个输出:

b'this is good'
b'this is a good string'
b'this is good'
b'this is a good string'

【讨论】:

  • 老实说,我不知道我的问题更多的是抱怨还是诚实的问题,即设计妨碍了性能。感谢您的贡献。如果一周内没有人回答,我会给你奖励。
  • 我认为这是一个公平的问题,但与 .format 或 f-strings 相比,我不确定性能成本是多少。
  • .format 和 f-strings 需要一个 decode() 所以它会更糟。我在网上的其他帖子中读到,使用 unicode 的速度大约是使用字节速度的一半。所以并不可怕,但是对于很多工作负载来说,当你想要做的只是从其他字节中组合字节时,它会受到伤害,是的,答案是在组合之前处理所有输入,这是一个重大的改革。并且使用六个或其他一些助手不会解决任何性能下降问题。我知道希望是明确的,但请注意 print() 命令同时接受字节和 unicode(所以不完全)
  • 这会中断 unicode 字符串。根据上面的示例,它适用于实际上不包含 unicode 字符的 unicode 字符串,但通常不是。
猜你喜欢
  • 1970-01-01
  • 2015-10-11
  • 2011-05-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-03
相关资源
最近更新 更多