如何在Python 3.x中使用字典格式化字符串?

260

我非常喜欢使用字典来格式化字符串。这有助于我更好地阅读我正在使用的字符串格式,同时也让我利用现有的字典。例如:

class MyClass:
    def __init__(self):
        self.title = 'Title'

a = MyClass()
print 'The title is %(title)s' % a.__dict__

path = '/path/to/a/file'
print 'You put your file here: %(path)s' % locals()

然而,我无法弄清楚如何在Python 3.x中执行相同的语法(或者是否可能)。 我想要做以下操作

# Fails, KeyError 'latitude'
geopoint = {'latitude':41.123,'longitude':71.091}
print '{latitude} {longitude}'.format(geopoint)

# Succeeds
print '{latitude} {longitude}'.format(latitude=41.123,longitude=71.091)
9个回答

509

这对您来说好吗?

geopoint = {'latitude':41.123,'longitude':71.091}
print('{latitude} {longitude}'.format(**geopoint))

3
尝试过这个方法,它有效。但我不理解“指针表示法”的用法。我知道Python不使用指针,这是否是kwargs的一个例子? - Homunculus Reticulli
4
@HomunculusReticulli,那是一个格式参数(最小字段宽度),不是C++风格的指向指针的指针。http://docs.python.org/release/2.4.4/lib/typesseq-strings.html - D.Rosado
36
Python 3.2 引入了 format_map。它类似于 str.format(**mapping),但直接使用 mapping 而不是复制到一个 dict。如果 mapping 是字典的子类,则这很有用。 - diapir
1
@eugene ** 对 Python 字典有什么作用?我认为它不会创建一个对象,因为 print(**geopoint) 会导致语法错误。 - Nityesh Agarwal
9
它将字典中的名称=值对作为单独的参数传递,例如print(**geopoint)print(longitude=71.091, latitude=41.123)相同。在许多语言中,它被称为splat操作符。在JavaScript中,它被称为spread操作符。在Python中,此操作符没有特定的名称。 - abhisekp
@NityeshAgarwal 这对我也是新鲜事。这很 - Samie Bencherif

88

由于 Python 3.0 和 3.1 已经停止更新且无人使用,因此您应该使用str.format_map(mapping) (Python 3.2+)。

str.format(**mapping)类似,但直接使用 mapping 而不是复制到 dict 中。如果 mapping 是 dict 子类,则这很有用。

这意味着您可以例如使用一个 defaultdict 来设置(并返回)缺失键的默认值:

>>> from collections import defaultdict
>>> vals = defaultdict(lambda: '<unset>', {'bar': 'baz'})
>>> 'foo is {foo} and bar is {bar}'.format_map(vals)
'foo is <unset> and bar is baz'
即使提供的映射是一个字典(dict),而不是子类,这可能仍然会略微更快。但由于差异并不大,因此......
>>> d = dict(foo='x', bar='y', baz='z')
>>> 'foo is {foo}, bar is {bar} and baz is {baz}'.format_map(d)

比...快约10纳秒(2%)。

>>> 'foo is {foo}, bar is {bar} and baz is {baz}'.format(**d)

在我的Python 3.4.3上,差异可能会随着字典中键的增加而变大。


请注意,格式语言要比这个灵活得多;它们可以包含索引表达式、属性访问等等,因此您可以对一个整个对象或两个对象进行格式化:

>>> p1 = {'latitude':41.123,'longitude':71.091}
>>> p2 = {'latitude':56.456,'longitude':23.456}
>>> '{0[latitude]} {0[longitude]} - {1[latitude]} {1[longitude]}'.format(p1, p2)
'41.123 71.091 - 56.456 23.456'

从3.6版本开始,您也可以使用插值字符串:

>>> f'lat:{p1["latitude"]} lng:{p1["longitude"]}'
'lat:41.123 lng:71.091'

您只需要记住在嵌套引号中使用其他引号字符。这种方法的另一个好处是比调用格式化方法快得多。


很好,相比于format,有任何性能上的提升吗?(假设它没有被复制到字典中) - Bhargav Rao
2
@BhargavRao 不多,2% :D - Antti Haapala -- Слава Україні
@BhargavRao 如果你想要更好的性能,可以使用 '%(latitude)s %(longitude)s'%geopoint ;) - Tcll

88

要将字典解包为关键字参数,请使用**。此外,新式格式化支持引用对象属性和映射项:

'{0[latitude]} {0[longitude]}'.format(geopoint)
'The title is {0.title}s'.format(a) # the a from your first example

3
我认为这个答案更好,因为为占位符添加位置索引可以使代码更加明确、易于使用。特别是如果有像这样的内容: '{0[latitude]} {1[latitude]} {0[longitude]} {1[longitude]}'.format(geopoint0, geopoint1) - Løiten
1
如果您正在使用defaultdict并且没有所有键,则此功能非常有用。 - Whymarrh

40

由于问题特定于Python 3,因此这里使用自Python 3.6以来可用的新f字符串语法

>>> geopoint = {'latitude':41.123,'longitude':71.091}
>>> print(f'{geopoint["latitude"]} {geopoint["longitude"]}')
41.123 71.091

注意外面的单引号和里面的双引号(你也可以反过来做)。


40
print("{latitude} {longitude}".format(**geopoint))

1
谢谢!我不知道为什么这个没有更多的投票。 - JohnMudd
2
这是最简单的选择,说实话。这也是将列表转换为可变参数的类似方式。这应该是正确的方法。 - Aleksandr Panzin

7

Python 2的语法也适用于Python 3:

>>> class MyClass:
...     def __init__(self):
...         self.title = 'Title'
... 
>>> a = MyClass()
>>> print('The title is %(title)s' % a.__dict__)
The title is Title
>>> 
>>> path = '/path/to/a/file'
>>> print('You put your file here: %(path)s' % locals())
You put your file here: /path/to/a/file

此外,它的性能也明显优于 f"""".format() ;) - Tcll

4
使用 format_map 实现你想要的功能。
print('{latitude} {longitude}'.format_map(geopoint))

这种方法的优点是:

  • 不需要将字典膨胀成参数(与 **geopoint相比),
  • 格式化字符串只能访问提供的映射,而不能访问整个变量范围(与 F-strings 相比)。

4
geopoint = {'latitude':41.123,'longitude':71.091}

# working examples.
print(f'{geopoint["latitude"]} {geopoint["longitude"]}') # from above answer
print('{geopoint[latitude]} {geopoint[longitude]}'.format(geopoint=geopoint)) # alternate for format method  (including dict name in string).
print('%(latitude)s %(longitude)s'%geopoint) # thanks @tcll

1
你错过了一个 ;) print('%(latitude)s %(longitude)s'%geopoint) 这也比其他两种方法快得多。 - Tcll
@tcll 实际上我想要的是示例,其中我可以在字符串内使用字典名称。类似于 '%(geopoint["latitude"])s %(geopoint["longitude"])s'%{"geopoint":geopoint} - Sheikh Abdul Wahid

2

大多数答案仅格式化字典的值。

如果您想将键也格式化为字符串,可以使用dict.items()

最初的回答

geopoint = {'latitude':41.123,'longitude':71.091}
print("{} {}".format(*geopoint.items()))

输出:

('纬度',41.123) ('经度',71.091)

如果您想以任意方式格式化输出,即不显示元组中的键值:

如果您想按照任意方式格式化输出,即不显示元组中的键值:

from functools import reduce
print("{} is {} and {} is {}".format(*reduce((lambda x, y: x + y), [list(item) for item in geopoint.items()])))

输出:

纬度为41.123,经度为71.091


请注意,从geopoint.items()中经度可能会在纬度之前出现;) - Tcll

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