<< >>

diveintopython.org |

Python for experienced programmers

## 3.12. 一次赋多个值

在Python中最酷的程序简写之一就是使用序列一次赋多个值。

例 3.24. 一次赋多个值

    >>> v = ('a', 'b', 'e')
    >>> (x, y, z) = v
    >>> x
    'a'
    >>> y
    'b'
    >>> z
    'e'

| `v` 是一个三元素的序列，`(x, y, z)` 是一个有三个变量的序列。将一个序列赋给另一个，会将 `v` 的每个值依次赋给每个变量。

它有着许多的用处。当构建可重用的模块时，你经常需要给一个名字赋以一系列的值。在C或C++中，你将使用 `enum` 并且手工地列出每个常量和它所对应的值，当值是连续的时候显得特别烦琐。在Python中，你可以使用内置的 `range` 函数来迅速地给多个变量赋予连续值。

例 3.25. 赋连续值

    >>> range(7)
    [0, 1, 2, 3, 4, 5, 6]
    >>> (MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) = range(7)
    >>> MONDAY
    0
    >>> TUESDAY
    1
    >>> SUNDAY
    6

| 内置的 `range` 函数返回一个整数列表。在这个最简单的形式中，它接受一个上限，并返回一个从0开始计数但不包括上限的一个列表。(如果你喜欢，你可以传递其它的参数来指定一个0以外的基数和1以外的步长。可以执行 `print range.__doc__` 来了解更多细节。)
| `MONDAY`，`TUESDAY`，`WEDNESDAY`，`THURSDAY`，`FRIDAY`，`SATURDAY`，和 `SUNDAY` 是我们定义的变量。(这个例子来自 `calendar` 模块，一个有趣的用来打印日历的小模块，就象UNIX程序 `cal`。`calendar` 模块定义了星期每天的整数常量。)
| 现在每个变量都有了它的值：`MONDAY` 是 `0`，`TUESDAY` 是 `1`，等等。

使用这种技术，你可以构建返回多值的函数，只要通过返回包含所有值的一个序列。调用者可以把返回值看成一个序列，或者将值赋给单个变量。

例 3.26. 从函数中返回多值

    >>> import os
    >>> os.path.split("/music/ap/mahadeva.mp3")
    ('/music/ap', 'mahadeva.mp3')
    >>> (filepath, filename) = os.path.split("/music/ap/mahadeva.mp3")
    >>> filepath
    '/music/ap'
    >>> filename
    'mahadeva.mp3'
    >>> (shortname, extension) = os.path.splitext(filename)
    >>> shortname
    'mahadeva'
    >>> extension
    '.mp3'

| `os` 模块有许多有用的函数，可用来操作文件和目录，并且 `os.path` 拥有操作文件路径的函数。`split` 函数可以分割整个路径，并返回一个包括路径和文件名的序列。
| 我们将 `split` 函数的返回值赋给一个有两个变量的序列。每个变量从返回的序列中接收相对应的值。
| 第一个变量，`filepath`，接收从 `split` 返回序列的第一个元素的值，文件路径。
| 第二个变量，`filename`, 接收从 `split` 返回序列的第二个元素的值，文件名。
| `os.path` 还包含了一个 `splitext` 函数，它可以分割一个文件名，返回一个包含文件名和文件扩展名的序列。我们使用同样的技术来将它们各自赋值给个别的变量。

只要有可能，你最好使用在 `os` 和 `os.path` 中的函数来处理对文件，目录，和路径的操作。这些模块是对于平台特定模块的封装产物，所以象 `os.path.split` 之类的函数可以工作在UNIX，Windows，MacOS，和其它任何支持Python的平台上。

利用多变量赋值甚至有更多可以做的。它可以用在遍历一个序列列表的时候，意味着你可将它用于 `for` 循环和列表映射中。你也许不认为序列列表是你每天都要遇到的东西，但是实际上字典的 `items` 方法就返回一个序列列表，每个序列的形式为`(key，value)`。所以多变量赋值允许你通过简单的方法来遍历字典的元素。

例 3.27. 遍历字典

    >>> for k, v in os.environ.items()
    ...     print "%s=%s" % (k, v)
    USERPROFILE=C:\Documents and Settings\mpilgrim
    OS=Windows_NT
    PROCESSOR_IDENTIFIER=x86 Family 6 Model 6 Stepping 10, GenuineIntel
    COMPUTERNAME=MPILGRIM
    USERNAME=mpilgrim

    […snip…]

| `os.environ` 是一个在你的系统中所定义的环境变量的字典。在Windows下，它们是你的用户和系统变量，可以容易地从MS-DOS中得到。在UNIX下，它们是输出(export)到你的shell启动脚本的变量。在MacOS下，没有环境变量的概念，所以这个字典为空。
| `os.environ.items()` 返回一个序列列表：`[(key1，value1)，(key2，value2)，...]`。`for` 循环遍历这个列表。第一轮，它将 `key1` 赋给 `k` 而 `value1` 赋给 `v`，所以 `k` = `USERPROFILE` 而 `v` = `C:\Documents and Settings\mpilgrim`。第二轮，`k` 得到第二个关键字，`OS`，而 `v` 得到相对的值，`Windows_NT`。

使用多变量赋值不是绝对必需的。它是一种方便的简写，且可以让你的代码更加可读，特别是当处理字典时(通过 `items` 方法)。但是如果发现你迫使自已的代码通过种种周折(为了以正确的形式得到数据)，只是为了让你可以一次给两个变量赋值，可能就不值得那么做了。

例 3.28. 通过多变量赋值进行字典映射

    >>> print "\n".join(["%s=%s" % (k, v) for k, v in os.environ.items()])
    USERPROFILE=C:\Documents and Settings\mpilgrim
    OS=Windows_NT
    PROCESSOR_IDENTIFIER=x86 Family 6 Model 6 Stepping 10, GenuineIntel
    COMPUTERNAME=MPILGRIM
    USERNAME=mpilgrim

    […snip…]

| 多变量赋值也可以用于列表映射，使用这种简捷的方法来将字典映射成列表。本例中，我们通过将列表连接成一个字符串使得这种用法更深一步。注意它的输出与前例中的 `for` 循环一样。这就是为什么你在Python中看到那么少的 `for` 循环的原因；许多复杂的事情可以不用它们完成。你可以讨论是否这种方法更易读，但是它相当快，因为只有一条输出语句而不是许多。

例 3.29. 在 `MP3FileInfo` 中的多变量 `for` 循环

        tagDataMap = {"title"   : (  3,  33, stripnulls),
                      "artist"  : ( 33,  63, stripnulls),
                      "album"   : ( 63,  93, stripnulls),
                      "year"    : ( 93,  97, stripnulls),
                      "comment" : ( 97, 126, stripnulls),
                      "genre"   : (127, 128, ord)}
        .
        .
        .
                if tagdata[:3] == "TAG":
                    for tag, (start, end, parseFunc) in self.tagDataMap.items():
                        self[tag] = parseFunc(tagdata[start:end])

| `tagDataMap` 是一个类属性，它定义了我们正在一个MP3文件中所查找的标记。标记被保存在定长的字段中；一旦我们读出文件的最后128个字节，字节3到32是歌曲的题目，33-62是歌手名字，63-92是专集名字，等等。注意 `tagDataMap` 是一个序列字典，每个序列包含两个整数和一个函数引用。
| 这个看上去有些复杂，其实不是。`for` 变量结构与通过 `items` 返回的列表元素的结构相匹配。记住，`items` 返回一个形式为`(key，value)`的序列列表。列表的第一个元素是`("title", (3, 33, <function stripnulls>))`，所以循环的第一轮，`tag` 得到 `"title"`，`start` 得到 `3`，`end` 得到 `33`，而 `parseFunc` 得到函数 `stripnulls`。
| 现在我们已经提取出了单个MP3标记的所有参数，保存标记数据很容易。我们从 `start` 到`end` 划分 `tagdata` 以得到这个标记的实际数据，调用 `parseFunc` 来对数据进行后续处理，然后将它作为关键字的值赋给伪字典 `self` 的 `tag` 关键字。在遍历了 `tagDataMap` 中所有元素之后， `self` 拥有所有标记的值，并且你知道那看上去象什么。

进一步阅读 |

- How to Think Like a Computer Scientist 展示了如何使用多个赋值来交换两个变量的值。
- Python Knowledge Base 回答了关于 `os` 模块的问题。

« for 循环 | 更多关于模块 »