在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 拥有所有标记的值,并且你知道那看上去象什么。 |
进一步阅读 |