3.12. 一次赋多个值
在Python中最酷的程序简写之一就是使用序列一次赋多个值。
>>> v = ('a', 'b', 'e')
>>> (x, y, z) = v
>>> x
'a'
>>> y
'b'
>>> z
'e'
v 是一个三元素的序列,(x, y, z) 是一个有三个变量的序列。将一个序列赋给另一个,会将 v 的每个值依次赋给每个变量。 |
它有着许多的用处。当构建可重用的模块时,你经常需要给一个名字赋以一系列的值。在C或C++中,你将使用 enum 并且手工地列出每个常量和它所对应的值,当值是连续的时候显得特别烦琐。在Python中,你可以使用内置的 range 函数来迅速地给多个变量赋予连续值。
>>> range(7)
[0, 1, 2, 3, 4, 5, 6]
>>> (MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) = range(7)
>>> MONDAY
0
>>> TUESDAY
1
>>> SUNDAY
6
使用这种技术,你可以构建返回多值的函数,只要通过返回包含所有值的一个序列。调用者可以把返回值看成一个序列,或者将值赋给单个变量。
>>> 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 中的函数来处理对文件,目录,和路径的操作。这些模块是对于平台特定模块的封装产物,所以象 os.path.split 之类的函数可以工作在UNIX,Windows,MacOS,和其它任何支持Python的平台上。 |
利用多变量赋值甚至有更多可以做的。它可以用在遍历一个序列列表的时候,意味着你可将它用于 for 循环和列表映射中。你也许不认为序列列表是你每天都要遇到的东西,但是实际上字典的 items 方法就返回一个序列列表,每个序列的形式为(key,value)。所以多变量赋值允许你通过简单的方法来遍历字典的元素。
>>> 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…]
使用多变量赋值不是绝对必需的。它是一种方便的简写,且可以让你的代码更加可读,特别是当处理字典时(通过 items 方法)。但是如果发现你迫使自已的代码通过种种周折(为了以正确的形式得到数据),只是为了让你可以一次给两个变量赋值,可能就不值得那么做了。 |
>>> 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 模块的问题。