8.6. 基于 dictionary 的字符串格式化
Description: Python from novice to pro
Keywords: Python, Dive Into Python, tutorial, object-oriented, programming, documentation, book, free

# 深入 Python :Dive Into Python 中文版 Python 从新手到专家 [Dip_5.4b_CPyUG_Release] | Find:

## 8.6. 基于 dictionary 的字符串格式化

为什么学习 `locals` 和 `globals`？因为接下来就可以学习关于基于 dictionary 的字符串格式化。或许您还能记起，字符串格式化提供了一种将值插入字符串中的一种便捷的方法。值被列在一个 tuple 中，按照顺序插入到字符串中每个格式化标记所在的位置上。尽管这种做法效率高，但还不是最容易阅读的代码，特别是当插入多个值的时候。仅用眼看一遍字符串，您不能马上就明白结果是什么；您需要经常地在字符串和值的 tuple 之间进行反复查看。

有另外一种字符串格式化的形式，它使用 dictionary 而不是值的 tuple。

### 例 8.13. 基于 dictionary 的字符串格式化介绍

    >>> params = {"server":"mpilgrim", "database":"master", "uid":"sa", "pwd":"secret"}
    >>> "%(pwd)s" % params
    'secret'
    >>> "%(pwd)s is not a good password for %(uid)s" % params
    'secret is not a good password for sa'
    >>> "%(database)s of mind, %(database)s of body" % params
    'master of mind, master of body'

| 这种字符串格式化形式不用显式的值的 tuple，而是使用一个 dictionary，`params`。并且标记也不是在字符串中的一个简单 `%s`，而是包含了一个用括号包围起来的名字。这个名字是 `params` dictionary 中的一个键字，所以 `%(pwd)s` 标记被替换成相应的值 `secret`。
| 基于 dictionary 的字符串格式化可用于任意数量的有名的键字。每个键字必须在一个给定的 dictionary 中存在，否则这个格式化操作将失败并引发一个 `KeyError` 的异常。
| 您甚至可以两次指定同一键字，每个键字出现之处将被同一个值所替换。

那么为什么您偏要使用基于 dictionary 的字符串格式化呢？的确，仅为了进行字符串格式化，就事先创建一个有键字和值的 dictionary 看上去的确有些小题大作。它的真正最大用处是当您碰巧已经有了像 `locals` 一样的有意义的键字和值的 dictionary 的时候。

### 例 8.14. `BaseHTMLProcessor.py` 中的基于 dictionary 的字符串格式化

        def handle_comment(self, text):
            self.pieces.append("<!--%(text)s-->" % locals())

| 使用内置的 `locals` 函数是最普通的基于 dictionary 的字符串格式化的应用。这就是说您可以在您的字符串 (本例中是 `text`，它作为一个参数传递给类方法) 中使用局部变量的名字，并且每个命名的变量将会被它的值替换。如果 `text` 是 `'Begin page footer'`，字符串格式化 `"<!--%(text)s-->" % locals()` 将得到字符串 `'<!--Begin page footer-->'`。

### 例 8.15. 基于 dictionary 的字符串格式化的更多内容

        def unknown_starttag(self, tag, attrs):
            strattrs = "".join([' %s="%s"' % (key, value) for key, value in attrs])
            self.pieces.append("<%(tag)s%(strattrs)s>" % locals())

| 当这个模块被调用时，`attrs` 是一个键/值 tuple 的 list，就像一个 dictionary 的 `items`。这就意味着我们可以使用多变量赋值来遍历它。到现在这将是一种熟悉的模式，但是这里有很多东西，让我们分开来看： 假设 `attrs` 是 `[('href', 'index.html'), ('title', 'Go to home page')]`。 在这个列表解析的第一轮循环中，`key` 将为 `'href'`，`value` 将为 `'index.html'`。 字符串格式化 `' %s="%s"' % (key, value)` 将生成 `' href="index.html"'`。这个字符串就作为这个列表解析返回值的第一个元素。 在第二轮中，`key` 将为 `'title'`，`value` 将为 `'Go to home page'`。 字符串格式化将生成 `' title="Go to home page"'`。 这个 list 理解返回两个生成的字符串 list，并且 `strattrs` 将把这个 list 的两个元素连接在一起形成 `' href="index.html" title="Go to home page"'`。
| 现在，使用基于 dictionary 的字符串格式化，我们将 `tag` 和 `strattrs` 的值插入到一个字符串中。所以，如果 `tag` 是 `'a'`，最终的结果会是 `'<a href="index.html" title="Go to home page">'`，并且这就是追加到 `self.pieces` 后面的东西。

使用 `locals` 来应用基于 dictionary 的字符串格式化是一种方便的作法，它可以使复杂的字符串格式化表达式更易读。但它需要花费一定的代价。在调用 `locals` 方面有一点性能上的问题，这是由于 `locals` 创建了局部名字空间的一个拷贝引起的。