【问题标题】:subclass string.Formatter子类 string.Formatter
【发布时间】:2014-02-09 20:05:31
【问题描述】:

这里有一句话:How to define a new string formatter,我尝试子类化string.Formatter。这是我所做的。不幸的是,我似乎在这个过程中打破了它

import string
from math import floor, log10

class CustFormatter(string.Formatter):
    "Defines special formatting"
    def __init__(self):
        super(CustFormatter, self).__init__()

    def powerise10(self, x):
        if x == 0: return 0, 0
        Neg = x < 0
        if Neg: x = -x
        a = 1.0 * x / 10**(floor(log10(x)))
        b = int(floor(log10(x)))
        if Neg: a = -a
        return a, b

    def eng(self, x):
        a, b = self.powerise10(x)
        if -3 < b < 3: return "%.4g" % x
        a = a * 10**(b%3)
        b = b - b%3
        return "%.4g*10^%s" % (a, b)

    def format_field(self, value, format_string):
      # handle an invalid format
      if format_string == "i":
          return self.eng(value)
      else:
          return super(CustFormatter,self).format_field(value, format_string)

fmt = CustFormatter()
print('{}'.format(0.055412))
print(fmt.format("{0:i} ", 55654654231654))
print(fmt.format("{} ", 0.00254641))

就像在最后一行一样,我没有按位置引用变量,我得到一个KeyError。它显然期望在原始类中是可选的键,但我不明白为什么,我不确定我做错了什么。

【问题讨论】:

    标签: python string stringtemplate


    【解决方案1】:

    str.formatdoes auto numbering,而string.Formatter 没有。

    修改 __init__ 并覆盖 get_value 即可解决问题。

    def __init__(self):
        super(CustFormatter, self).__init__()
        self.last_number = 0
    
    def get_value(self, key, args, kwargs):
        if key == '':
            key = self.last_number
            self.last_number += 1
        return super(CustFormatter, self).get_value(key, args, kwargs)
    

    顺便说一句,上面的代码并没有严格模仿str.format 的行为。 str.format 抱怨如果我们将自动编号与手动编号混合使用,但上面没有。

    >>> '{} {1}'.format(1, 2)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ValueError: cannot switch from automatic field numbering to manual field specification
    >>> '{0} {}'.format(1, 2)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ValueError: cannot switch from manual field specification to automatic field numbering
    

    【讨论】:

    • 顺便说一句,自 Python 3.6 起,自动编号似乎已原生添加到 string.Formatter,请参阅此提交:7ce9074
    【解决方案2】:

    好消息:你没有做错任何事。 坏消息:这就是string.Formatter 的行为方式,它不支持类似{} 的位置格式。因此,即使没有任何子类化,最后一次调用也会失败。好消息:这可以通过覆盖 parse 方法来解决:

    import string
    
    class CF(string.Formatter):
        def parse(self, s):
            position = 0
            for lit, name, spec, conv in super(CF, self).parse(s):
                if not name:
                    name = str(position)
                    position += 1
                yield lit, name, spec, conv
    

    坏消息...啊,不,基本上就是这样:

    >>> CF().format('{} {}!', 'Hello', 'world')
    'Hello world!'
    

    【讨论】:

    • 谢谢,您知道上一张海报提供的覆盖 parse 或 get_value 的 + 和 - 是什么吗?还是等价的?
    • @Cambium falsetru 的版本对我来说看起来更具可读性,此外,您需要少重写一个方法(尽管必须重写构造函数并引入一个属性)。他对混合样式的建议也适用于我的代码(不过,您可以扩展任一版本来解决这个问题)。
    猜你喜欢
    • 1970-01-01
    • 2014-09-10
    • 1970-01-01
    • 2015-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多