2009年3月18日星期三

Python Looping小技巧

Python下面的循环有很多看起来花哨其实很实用的技巧。首当其冲的就是enumerate(),花哨到用语言很难讲清楚...小例子如下:

for i, v in enumerate(['tic', 'tac', 'toe']):
print i, v

0 tic
1 tac
2 toe

在编程的时候大家经常要用到的情况,就是控制循环的index同时是用来访问某一个数组的。比如用Matlab实现:

arr = {'tic', 'tac', 'toe'};

for i = 1:length(arr)
fprintf('%d %s', i, arr{i});
end

相比之下,大家就知道我为什么说enumerate()实在是很fancy的一个东西。

但enumerate()和我们一直理解的循环有一个很重要的区别,那就是循环的进行不是靠i的值控制的,而是靠enumerate来控制的。咱们写循环的时候一个常见的错误就是循环套循环的时候经常用一样的变量“i”来控制,这样就会出很诡异很诡异的问题,debug都不容易找到。但是在Python下面:

for i, season in enumerate(['Spring', 'Summer', 'Fall', 'Winter']):
print i, season
for i, day in enumerate(['Wed', 'Thu', 'Fri', 'Sat']):
print i, day

0 Spring
0 Wed
1 Thu
2 Fri
3 Sat
1 Summer
0 Wed
1 Thu
2 Fri
3 Sat
2 Fall
0 Wed
1 Thu
2 Fri
3 Sat
3 Winter
0 Wed
1 Thu
2 Fri
3 Sat

虽然我在两层循环用了一样的i,但Python根本不鸟这个,只管用enumerate()生成(index, arr[index])对,生成完之后循环结束。如果我用matlab这样写:

S = {'Spring', 'Summer', 'Fall', 'Winter'};
D = {'Wed', 'Thu', 'Fri', 'Sat'};

for i = 1:length(S)
disp(S{i});
for i = 1:length(D)
disp(D{i});
end
end

大家都知道什么后果了吧...

Enumerate还有一个非常Fancy的例子:

fp = open("file", "r")
for i, line in enumerate(fp):
print "line number: " + lineno + ": " + line.rstrip()

可以一行一行把一个文件读出来,而且很省内存,只要你的内存能装下那一行就行了。在需要把一个超大文本文件里的数据读出来处理的时候,这个例子非常有用。

最后再帖一个enumerate之外控制循环的例子,也是相当fancy的:

questions = ['name', 'quest', 'favorite color']
answers = ['lancelot', 'the holy grail', 'blue']
for q, a in zip(questions, answers):
print 'What is your ' + q + '?', 'It is ' + a + '.'

What is your name? It is lancelot.
What is your quest? It is the holy grail.
What is your favorite color? It is blue.