diveintopython.org
Python for experienced programmers

3.12. 一次赋多个值

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

例 3.24. 一次赋多个值

>>> v = ('a', 'b', 'e')
>>> (x, y, z) = v 1
>>> x
'a'
>>> y
'b'
>>> z
'e'
1 v 是一个三元素的序列,(x, y, z) 是一个有三个变量的序列。将一个序列赋给另一个,会将 v 的每个值依次赋给每个变量。

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

例 3.25. 赋连续值

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

内置的 range 函数返回一个整数列表。在这个最简单的形式中,它接受一个上限,并返回一个从0开始计数但不包括上限的一个列表。(如果你喜欢,你可以传递其它的参数来指定一个0以外的基数和1以外的步长。可以执行 print range.__doc__ 来了解更多细节。)

2 MONDAYTUESDAYWEDNESDAYTHURSDAYFRIDAYSATURDAY,和 SUNDAY 是我们定义的变量。(这个例子来自 calendar 模块,一个有趣的用来打印日历的小模块,就象UNIX程序 calcalendar 模块定义了星期每天的整数常量。)
3 现在每个变量都有了它的值:MONDAY0TUESDAY1,等等。

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

例 3.26. 从函数中返回多值

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

os 模块有许多有用的函数,可用来操作文件和目录,并且 os.path 拥有操作文件路径的函数。split 函数可以分割整个路径,并返回一个包括路径和文件名的序列。

2

我们将 split 函数的返回值赋给一个有两个变量的序列。每个变量从返回的序列中接收相对应的值。

3 第一个变量,filepath,接收从 split 返回序列的第一个元素的值,文件路径。
4 第二个变量,filename, 接收从 split 返回序列的第二个元素的值,文件名。
5 os.path 还包含了一个 splitext 函数,它可以分割一个文件名,返回一个包含文件名和文件扩展名的序列。我们使用同样的技术来将它们各自赋值给个别的变量。
Note

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

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

例 3.27. 遍历字典

>>> for k, v in os.environ.items() 1 2
...     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…]
1

os.environ 是一个在你的系统中所定义的环境变量的字典。在Windows下,它们是你的用户和系统变量,可以容易地从MS-DOS中得到。在UNIX下,它们是输出(export)到你的shell启动脚本的变量。在MacOS下,没有环境变量的概念,所以这个字典为空。

2 os.environ.items() 返回一个序列列表:[(key1value1),(key2value2),...]for 循环遍历这个列表。第一轮,它将 key1 赋给 kvalue1 赋给 v,所以 k = USERPROFILEv = C:\Documents and Settings\mpilgrim。第二轮,k 得到第二个关键字,OS,而 v 得到相对的值,Windows_NT
Note

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

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

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

[…snip…]
1

多变量赋值也可以用于列表映射,使用这种简捷的方法来将字典映射成列表。本例中,我们通过将列表连接成一个字符串使得这种用法更深一步。注意它的输出与前例中的 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)} 1
    .
    .
    .
            if tagdata[:3] == "TAG":
                for tag, (start, end, parseFunc) in self.tagDataMap.items(): 2
                    self[tag] = parseFunc(tagdata[start:end])                3
1

tagDataMap 是一个类属性,它定义了我们正在一个MP3文件中所查找的标记。标记被保存在定长的字段中;一旦我们读出文件的最后128个字节,字节3到32是歌曲的题目,33-62是歌手名字,63-92是专集名字,等等。注意 tagDataMap 是一个序列字典,每个序列包含两个整数和一个函数引用。

2

这个看上去有些复杂,其实不是。for 变量结构与通过 items 返回的列表元素的结构相匹配。记住,items 返回一个形式为(keyvalue)的序列列表。列表的第一个元素是("title", (3, 33, <function stripnulls>)),所以循环的第一轮,tag 得到 "title"start 得到 3end 得到 33,而 parseFunc 得到函数 stripnulls

3

现在我们已经提取出了单个MP3标记的所有参数,保存标记数据很容易。我们从 startend 划分 tagdata 以得到这个标记的实际数据,调用 parseFunc 来对数据进行后续处理,然后将它作为关键字的值赋给伪字典 selftag 关键字。在遍历了 tagDataMap 中所有元素之后, self 拥有所有标记的值,并且你知道那看上去象什么


进一步阅读