如何用相同的代码满足不同的字符串格式化场景?

How can I satisfy different string formatting scenarios with the same code?

我有一个包含占位符文本字符串和关联值的输入文件。我并不真正提前知道哪些是即将到来的,并且很好奇是否有一个代码块可以满足以下所有占位符字符串和提供的值的组合。

本质上我想要这个:

examples = [
    {"text": "There are {} bagels in a bakers dozen.", "values": 13},
    {"text": "My favorite bagels are {} and {}.", "values": ["cinnamon raisin", "maple"]},
    {"text": "I have not had a pop tart in 14 {}", "values": "years"}
]

for single_example in examples:
    print(single_example['text'].format(single_example['values']))

但是 format(single_example['values']) 不适用于第二个 text。相反,我可以执行 format(*single_example['values']) 以允许第二个示例工作,但它会因错误 IndexError: tuple index out of range 和第三个字符串切片而中断第一个示例。

我想我需要将所有内容都放入一个列表中,这样 format(*single_example['values']) 才能全面发挥作用,但我一直在寻找一种适用于上述情况的方法。

这对我有用,但我觉得可能有更精简的方法。

for single_example in examples:
    if isinstance(single_example['values'], list):
        values = single_example['values']
    else:
        lst = []
        lst.append(str(single_example['values']))
        values = lst
    print(single_example['text'].format(*values))

您可以将所有值存储在一个列表中:

examples = [
    {"text": "There are {} bagels in a bakers dozen.", "values": [13]},
    {"text": "My favorite bagels are {} and {}.", "values": ["cinnamon raisin", "maple"]},
    {"text": "I have not had a pop tart in 14 {}", "values": ["years"]}
]

for single_example in examples:
    print(single_example['text'].format(*single_example['values']))

输出:

There are 13 bagels in a bakers dozen.
My favorite bagels are cinnamon raisin and maple.
I have not had a pop tart in 14 years

编辑

如果您无法控制输入数据,您可以根据需要转换值:

examples = [
    {"text": "There are {} bagels in a bakers dozen.", "values": 13},
    {"text": "My favorite bagels are {} and {}.", "values": ["cinnamon raisin", "maple"]},
    {"text": "I have not had a pop tart in 14 {}", "values": "years"}
]

for single_example in examples:
    values = single_example['values'] if isinstance(single_example['values'], list) else [single_example['values']]
    print(single_example['text'].format(*values))

为什么不使用 try except 子句:

for single_example in examples:
    try:
        print(single_example['text'].format(single_example['values']))
    except:
        print(single_example['text'].format(*single_example['values']))

输出:

There are 13 bagels in a bakers dozen.
My favorite bagels are cinnamon raisin and maple.
I have not had a pop tart in 14 years

如果您的输入无法更改,您可以在 format 中添加一个 if-else

examples = [
    {"text": "There are {} bagels in a bakers dozen.", "values": 13},
    {"text": "My favorite bagels are {} and {}.", "values": ["cinnamon raisin", "maple"]},
    {"text": "I have not had a pop tart in 14 {}", "values": "years"}
]

for single_example in examples:
    print(single_example['text'].format(*single_example['values'] if isinstance(single_example['values'], (list, tuple)) else [single_example['values']]))

# There are 13 bagels in a bakers dozen.
# My favorite bagels are cinnamon raisin and maple.
# I have not had a pop tart in 14 years

简化现有代码:

for single_example in examples:
    if isinstance(single_example['values'], list):
        values = single_example['values']
    else:
        values = [single_example['values']]
    print(single_example['text'].format(*values))

输出:

There are 13 bagels in a bakers dozen.
My favorite bagels are cinnamon raisin and maple.
I have not had a pop tart in 14 years

我认为下面的函数是检查值是否为“标量”并在其上分支的最佳方法。 Numpy 只是为此导入有点大,但它有效。

def as_iterable(x): 
    from numpy import isscalar 
    if isscalar(x):  # Includes numbers and strings
        yield x 
    else:            # Lists, tuples, etc
        yield from x

现在您可以这样做了:

examples = [ 
    {"text": "There are {} bagels in a bakers dozen.", "values": 13}, 
    {"text": "My favorite bagels are {} and {}.", "values": ["cinnamon raisin", "maple"]},
    {"text": "I have not had a pop tart in 14 {}", "values": "years"} 
] 

for single_example in examples: 
    print(single_example['text'].format(*as_iterable(single_example['values']))) 

与其他答案没有太大区别,但我会使用两个列表而不是单个字典并将两个列表压缩。然后对于示例列表的每个索引,相应地解压缩它们。

def format_custom_text(string, values):
    # Unpack the values
    return string.format(*values)


values = [
    13,
    ['cinnamon raisin', 'maple'],
    'years'
]

# You don't need a dictionary to store the examples
examples = [
    "There are {} bagels in a bakers dozen.",
    "My favorite bagels are {} and {}.",
    "I have not had a pop tart in 14 {}."
]

for single_example in zip(examples, values):
    # Convert each value inside the values list into a list
    placeholder_values = []
    if not type(single_example[1]) == list:
        placeholder_values.append(single_example[1])
    else:
        placeholder_values = single_example[1]