Python中可以用于迭代的对象都称之为迭代器,还有一类特殊的迭代器被称为生成器。例如list就可被当做迭代器,而range就是生成器(Python2.7中对应的是xrange)。生成器的迭代元素是在迭代时才会被计算出来,而不是一开始就全部计算出来,这样做可以节省内存,但是运算时间会变长。在介绍这些之前先补充一些list中容易遇到的坑(其实就是我遇到过的)。

我是做输运模拟的,所以写代码希望更数学一些,因而总是想和MATLAB、Fortran中的一些用法做对应,从而会有些对我而言比较奇怪的坑。

1
2
3
4
1    L = [[1,2,3], [4,5,6], [7,8,9]]
2 a = L[2] # a = [7,8,9]
3 b = L[:][2] # b = [7,8,9]
4 c = a.append(4) # c = None

上述a输出的是L的最后一个元素,结果就是[7,8,9],b这样写,我的本意是想输出[3,6,9],也就是说将这里的L看作为了一个矩阵,我想输出最后一列,但其实list仅仅是一个类似于一维数组的东西,只不过每个元素可以为任何数据类型,L仅仅是含有三个长度相同的list类型的元素的list而已。MATLAB和Fortran中为了简化书写,这样运算是没有问题的,而且也更数学一些。上述的L[:]就是L。numpy中的array可以执行相应操作。c的值为None,这是因为append函数是没有返回值的,list中大多的函数仅仅是最自身进行操作的,类似于C++中的this指针修改自身的私有变量值一样,类方法函数是可以没有返回值的。这里的等号要去“等”的是append的返回值,而不是a这个变量,从而c的值就为None。但在pandas中的DataFrame(df)的类方法一般会有返回值,返回值为修改后的df,而且默认不会改自身的值,若要修改自身的值则需要设置inplace关键字。

内嵌循环

1
2
3
4
5
6
1    L = []
2 for x in range(9):
3 L.append(x**2)
4 L = [x**2 for x in range(9)]
5 L = [x**2 for x in range(9) if x%2==0]
6 D = {x:x**2, for x in range(9)}

上述第2,3行就是通常意义上的循环,但是对于list和dict而言可以更简便一些,直接将循环放入[]或{}中,甚至可以加上一条判断语句,这里和Fortran中的用forall语句初始化数组的方法非常相似。

生成器和yield

我们知道Python中函数返回值的方式有两种,一种是通过return完成的,这个和其他语言无异,另一种方式是通过yield完成的,这里其实是利用def封装了一个生成器。

1
2
3
4
5
6
1    def square(L):
2 for x in L:
3 yield x**2
4 L = [1,2,3]
5 for x in square(L):
6 print(x) # 1, 4, 9

上述square函数就是一个生成器,每次返回一个值,然后下次访问这个函数的时候则会进行下个循环。相当于临时中断循环,再次访问的时候再继续循环。

zip、enumerate和map

这两个关键字是用来方便循环使用的,有时候我们要遍历一个list,但是同时还需要使用当前元素的指标,或要同时遍历两个以上的list。这时用这两个关键字会十分的方便。

1
2
3
4
5
6
7
8
9
10
1    L = [4,5,6,7]
2 for i, x in enumerate(L):
3 print(i, x) # 0 4,1 5,2 6,3 7
4 for x, y in zip([1,2],[3,4]):
5 print(x*y) # 3, 8
6 f = lambda x: x + 2
7 z = map(f, L)
8 z = map(lambda x:x+2, L)
9 for x in z:
10 print(x)

遍历enumerate,第一个元素是指标,第二个是值。zip可以循环多个list。map是用来实现向量化的,它的第一个参数是函数,第二个参数是可迭代变量,返回的迭代器。其中lambda是一种表达式,上述7,8行的语句是等价的。若想直接得到生成器中的所有结果,则可用list()函数。还有其他一些类似函数被定义在内建库itertools中,这里不再赘述。类似map功能的函数还有numpy中的vectorize函数。

set、dict

遍历list非常的方便,那么如何遍历set和dict类型的数据呢?set和list的遍历方式没有区别,但是要注意的是set中的元素是没有顺序的,也就是说每次遍历的结果可能是不一样的,类似的dict中的数据也是没有顺序的,若想遍历有顺序的dict,则需要利用collections中的OrderedDict。

1
2
3
4
1    for x in set([1,2,3])
2 print(x)
3 for s, x in {'a':1,'b',2}.items():
4 print(s, x)

上述两个输出在不同电脑上可能会是不同的。