使用f-string语法来解压缩带有花括号抑制的列表

78

我一直在使用新的f-string格式检查我的字符串格式选项。我经常需要解包长度未知的列表和其他可迭代对象。目前我使用以下方法...

>>> a = [1, 'a', 3, 'b']
>>> ("unpack a list: " + " {} "*len(a)).format(*a)
'unpack a list:  1  a  3  b '

虽然有点繁琐,但使用 pre-3.6 的 .format 符号可以完成工作。新的 f-string 格式选项很有趣,因为它支持运行时字符串拼接。我遇到的问题是在复制 {} 数量时。在我的先前示例中,我只需创建必要的结构并在 .format() 部分中解包即可。

尝试这样做会产生一个有效的变体,但:

1) 将两个花括号放在一起无法解包...

>>> 'unpack a list'  f' {{*a}}'
'unpack a list {*a}'

2) 在内部 {} 对周围添加空格:

这样可以实现目标,但会保留 { 和 } 的开头和结尾:

>>> 'unpack a list'  f' { {*a} }'
"unpack a list {1, 3, 'a', 'b'}"

2b) 将变量连接成一个f-string

这样做使得代码看起来更好,语法更清晰,因为显然是从左到右进行求值的。但是,这仍然保留了包含花括号:

>>> f'unpack a list { {*a} }'
"unpack a list {1, 3, 'a', 'b'}"

3) 只使用{a}尝试自动解包

也许我过于考虑整个过程,希望能够自动解包。但这只会得到用方括号[]代替花括号的列表表示:

>>> f'unpack a list {a}'
"unpack a list [1, 'a', 3, 'b']"
如何抑制上面变体(2)中的花括号,我必须继续使用现有的.format()方法吗?我想保持简单,使用f字符串提供的新功能,并且不会回退到比我当前熟悉的Python版本更早的版本。我开始怀疑f字符串并没有完全覆盖其.format()兄弟所提供的所有内容。我现在就说到这里,因为我甚至还没有涉及转义编码和无法在f字符串中使用\的问题。我已阅读PEP并进行了广泛搜索,但我觉得我可能忽略了显而易见的部分,或者我希望的目前不可能实现。

几个小时后编辑:

4) 使用下标手动删除括号:str(a)[1:-2]

我发现这个变体可以用于我需要的一些情况

f'unpack a list: {str(a)[1:-2]}'
"unpack a list: 1, 'a', 3, 'b"

但是切片仅仅是一种方便的方法,结果中仍然保留了字符串引号。

5) 并来自@SenhorLucas的最终解决方案

 a = np.arange(10)

print(f"{*a,}")
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

带有尾随逗号的解包。


3
顺便提一下,如果不明显的话:原因(1)无法工作是因为“{{”是你在f-string中转义大括号的方式,因此在那个例子中根本不进行插值。 - Zero Piraeus
1
f"unpack a list: {', '.join([str(x) for x in [*a]])}" returns 'unpack a list: 1, a, 3, b' - noobninja
然而,“','.join”比“format”更长,所以最好还是坚持使用“format” ;) - NaN
4
我有同样的反应,但最终我发现 Python 3.6 的 f-strings 并不意味着要取代 .format() 方法,而只是为了使常见情况更加简洁并增加功能;总会有需要使用 .format() 的情况,包括格式化列表/元组/集合/任意对象或嵌套对象。这并不是回到 pre-3.6 语法的情况;.format() 不打算被弃用以支持 f-strings。不可否认的是,f-string 的文档应该明确说明这一点。同时它也违反了 "做一件事情应该有且只有一种显而易见的方法" 这一原则。 - smci
4
在提问时,您不应该抄袭他人的答案。请接受答案并将完全的功劳留给他们。 - divenex
1
除了@divenex的评论之外,选择一个真正回答你的问题(如何删除大括号),而不是用圆括号替换大括号的答案。 - mins
5个回答

68

在展开的列表后面加一个逗号即可。

a = [1, 2, 3]
print(f"Unpacked list: {*a,}")
# Unpacked list: (1, 2, 3)

这个语法有更长的解释在 这个讨论串 中。


3
做到了,就是那个缺失的逗号。谢谢。 - NaN
4
注意:print(*foo)print(f'{*foo,}') 不会产生相同的输出,也不应该如此。详见链接了解更多信息。 - Leo
6
有没有办法省略括号? - Stevoisiak
3
你好,我有一个快速的问题,如何对列表中的元素进行四舍五入?{*list,:.2f} 似乎不起作用。 - Gerald
4
为什么会选择这个答案?实际上,print(f"Unpacked list: {*a,}") 将列表转换为元组,但不幸的是它并没有移除大括号,而是用圆括号代替了它们,输出结果为 Unpacked list: (1, 2, 3)。是否有一种更短的方法可以去掉大括号,而不像实际答案那样冗长? - mins

44

由于在 f-string 中大括号内允许任何有效的 Python 表达式 allowed,因此您可以简单地使用 str.join() 来生成所需的结果:

>>> a = [1, 'a', 3, 'b']
>>> f'unpack a list: {" ".join(str(x) for x in a)}'
'unpack a list: 1 a 3 b'

如果您的实际使用情况比上述更冗长,当然也可以编写帮助函数:

def unpack(s):
    return " ".join(map(str, s))  # map(), just for kicks

>>> f'unpack a list: {unpack(a)}'
'unpack a list: 1 a 3 b'

2
@Zero_Piraeus 感谢您的建议,但是它仍然比点格式化版本更不方便。 - NaN
5
很抱歉听到这个……你确定你不是对f-strings意外的变化有负面反应吗?对我来说,构建format字符串所需跳跃的步骤使它比这里提供的f-string替代方案难以阅读——在我看来,使用常见的Python习语,并且对任何阅读它的人来说都是显而易见的。 - Zero Piraeus
我对f-没有问题,但是我经常需要解开长度未知的列表、numpy和其他参数。这个...例如,("{} "*len(a)).format(*a) # a = [1,2,3,4]可以工作。然而,我找不到f-格式化的等效方法。它们都有各自的用处。文档中的示例过于简单。如果无法实现,那很好。在我的使用中,使用函数所获得的收益很小。似乎将两者混合使用的方法效果很好,但是第三个示例仍然遗留了那些花括号...这促使我最初提出问题。 - NaN
2
假设 a = [1, 2, 3, 4],如果想使用特定的格式,可以使用 "unpack a list: " + " ".join(f"{x:0.2f}" for x in a),但我同意 @NaN 的观点,这并不优雅。有一个语法 f"unpack a list: {*a:0.2f}" 可以为所有列表元素重复格式。我相信我错过了为什么不能实现这样的东西的原因... - divenex

1

简单的Python可能更清晰:

>>>  'unpack a list: ' + ' '.join(str(x) for x in a)
'unpack a list: 1 a 3 b'

通过切片操作:
>>> 'unpack a list: ' + ' '.join([str(x) for x in a][1:3])
'unpack a list: a 3'

1
谢谢。我知道解决方法,就像其他人所提到的那样。正是这个特定的例子……(“{}”*len(a)).format(*a) #例如,a=[1,2,3,4]可以工作……找不到f-格式化等效物。目前在f-格式化中没有实现,因此在解包未知大小的对象时,坚持常规路线或使用加入方法同样容易,该方法在Python 3.x的后续版本中引入了解包对象的便利性。 - NaN

1
我认为这不是使用f-Strings的正确方式。最好的做法是准备一个与print()兼容的元组,例如:
mixed = [1, "list_string", 2]
number = 23
answer = 46

info = 'Content:', *mixed, f'{number} {answer}'
print(*info)  # default sep=' '

输出

Content: 1 list_string 2 23 46

1
好的,但这假定 OP 的最终使用一定是 print(),而不仅仅是为了任何其他原因形成字符串表示,例如 __str__() 方法。 - smci

0

我之前做过这个,采用了牛津风格的逗号。

def unpack_list(lst):  # Oxford comma
    if not isinstance(lst, str):
        lst = [str(item) for item in lst]
    if len(lst) == 0:
        return
    if len(lst) == 1:
        return ", ".join(lst)
    if len(lst) == 2:
        return ", and ".join(lst) 
    else:
        first_part = lst[:-1]
        last_part = lst[-1]
        return ", ".join(first_part) + ", and " + last_part

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接