将格式化可迭代对象包含在更大的格式化字符串中

13 投票
3 回答
2547 浏览
提问于 2025-04-16 23:39

最近在写一个类的时候,我最开始加了一个 __repr__ 方法,内容大致是这样的:

return "{}({!r}, {!r}, {!r})".format(
                self.__class__.__name__,
                self.arg1,
                self.arg2,
                self.arg3)

像这样重复 '{!r}' 的写法让我觉得不太对,如果将来我再往这个类里添加更多参数,维护起来会很麻烦。不过,我想到的其他更稳妥的替代方案也没什么优雅之处。

我尝试用编程的方式来构建格式字符串:

fmt = "{}(%s)" % ", ".join(["{!r}"]*3)
return fmt.format(self.__class__.__name__,
                  self.arg1,
                  self.arg2,
                  self.arg3)

或者用 str.join 分别格式化参数:

args = ", ".join(map(repr, [self.arg1, self.arg2, self.arg3]))
return "{}({})".format(self.__class__.__name__, args)

目前我用最后一个例子实现了这个类,但我对以上这些方法都不是特别满意,所以想听听其他的建议。

更新: 受到Ned的回答启发,我现在在一个辅助模块里添加了以下这个工具函数:

def format_iter(iterable, fmt='{!r}', sep=', '):
    return sep.join(fmt.format(x) for x in iterable)

>>> format_iter(range(10))
'0, 1, 2, 3, 4, 5, 6, 7, 8, 9'
>>> format_iter(range(10), sep='|')
'0|1|2|3|4|5|6|7|8|9'
>>> format_iter(range(10), fmt='{:04b}', sep='|')
'0000|0001|0010|0011|0100|0101|0110|0111|1000|1001'
>>> format_iter(range(10), fmt='{0.real}+{0.imag}j')
'0+0j, 1+0j, 2+0j, 3+0j, 4+0j, 5+0j, 6+0j, 7+0j, 8+0j, 9+0j'

更新2: 我最终又添加了一个第二个工具函数,几乎和agf的回答中的那个一模一样:

def call_repr(name, *args):
    return "{}({})".format(name, format_iter(args))

所以最开始让我觉得不爽的 __repr__ 函数现在看起来是这样的:

def __repr__(self):
    return call_repr(self.__class__.__name__,
                     self.arg1,
                     self.arg2)

(是的,原来的一个构造函数参数今天早些时候去掉了。)

3 个回答

0

我可能会这样做:

def __repr__(self):
    return "{0}({1})".format(
        type(self).__name__,
        ", ".join(repr(arg) for arg in (self.arg1, self.arg2, self.arg3)))

或者:

_init_getter = operator.attrgetter('arg1', 'arg2', 'arg3')
def __repr__(self):
    return "{0}({1})".format(
        type(self).__name__,
        ", ".join(repr(arg) for arg in self._init_getter(self)))
8

我的建议是,如果你不喜欢这段代码,就把它放到一个函数里去:

def repr_all(*args):
    return ", ".join(repr(a) for a in args)


def __repr__(self):
    args = repr_all(self.arg1, self.arg2, self.arg3)
    return "{}({})".format(self.__class__.__name__, args)
9

如果这是一个你会经常用到的模式,我可能会使用:

# This is a static method or module-level function
def argrepr(name, *args):
    return '{}({})'.format(name, ', '.join(repr(arg) for arg in args))

def __repr__(self):
    return argrepr(self.__name__, self.arg1, self.arg2, self.arg3)

或者

# This is a static method or module-level function
def argrepr(*args):
    return '(' + ', '.join(repr(arg) for arg in args) + ')'

def __repr__(self):
    return repr(self.__name__) + argrepr(self.arg1, self.arg2, self.arg3)

撰写回答