Python高级特性-迭代(Iteration)-列表生成式-生成器

来源:互联网 发布:淘宝宝贝降权多久恢复 编辑:程序博客网 时间:2024/06/11 15:29

迭代


定义

for循环对list或tuple进行遍历我们称之为迭代(Iteration)

  1. 只要属于list类型的对象都可以被迭代.
  2. Python的for循环抽象程度要比Java的高,迭代不仅可以用在list或tuple上还可以用在其他迭代对象上.比如dict,str.
>>> d = {'a':1,'b':2,'c':3}>>> for x in d:...     print x... acb>>> str = 'abc'>>> for s in str:...    print s... abc

dict是无序的所以输出结果也无序.dict默认迭代的是key,如果要迭代value呢?可以使用for value in d.itervalues(),如果需要同时迭代key和value可以用for k, v in d.iteritems().

  1. 但是如何判断一个对象是否是可迭代对象呢?我们可以通过collection模块里的Iterable类进行判断.
>>> from collections import Iterable>>> isinstance('abc',Iterable)True>>> isinstance((1,2,3),Iterable)True>>> isinstance([1,2,3],Iterable)True>>> isinstance(123,Iterable)False
  1. for可以同时迭代两个变量
>>> for x,y in ((1,1),(2,2),(3,3)):...     print x ,y ... 1 12 23 3

开拓思维要是需要迭代2个以上的对象呢?

>>> for a,b,c in [(1,1,1),(2,2,2),(3,3,3)]:...     print a,b,c... 1 1 12 2 23 3 3

列表生成式


定义

列表生成式是Python内置的非常强大的可以用来创建list的生成式
例:生成[1…10]的list

>>> range(1,11)[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

但是如果生成[1x1,2x2,3x3….10x10]的list怎么办?
方法1:循环

>>> for x in range(1,11):...     y = x * x...     L.append(y)... >>> L[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]>>> 

方法2:列表生成式

>>> [x*x for x in range(1,11)][1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

x*x代表目标list的元素要放在最前面,后面就是跟for循环取值然后赋值给x带入x*x 进行运算.

生成式的其它用法

  1. for循环后添加if判断
>>> [x*x for x in range(1,11) if x%2 == 0] #筛选出偶数[4, 16, 36, 64, 100]>>> [x*x for x in range(1,11) if x%2 != 0] #筛选出奇数[1, 9, 25, 49, 81]#只输出符合if语句的内容
  1. 两层循环
>>> [m + n for m in 'abc' for n in '123']['a1', 'a2', 'a3', 'b1', 'b2', 'b3', 'c1', 'c2', 'c3']>>> [m + n for m in ['a','b','c'] for n in [1,2,3]]Traceback (most recent call last):  File "<stdin>", line 1, in <module>TypeError: cannot concatenate 'str' and 'int' objects>>> [m + n for m in ('a','b','c') for n in (1,2,3)]Traceback (most recent call last):  File "<stdin>", line 1, in <module>TypeError: cannot concatenate 'str' and 'int' objects#只能对同类型的数据进行组合>>> [m + n for m in ('a','b','c') for n in ('1','2','3')]['a1', 'a2', 'a3', 'b1', 'b2', 'b3', 'c1', 'c2', 'c3']>>> [m + n for m in ['a','b','c'] for n in ['1','2','3']]['a1', 'a2', 'a3', 'b1', 'b2', 'b3', 'c1', 'c2', 'c3']>>> [m + n for m in [1,2,3] for n in [4,5,6]][5, 6, 7, 6, 7, 8, 7, 8, 9] #以m为基础进行求和,逐次运行.

三层及三层以上的循环基本用不到了.

生成器


定义

所谓生成器就是列表生成器的进化版本:
列表生成器虽然方便创建数组但是也会占用大量内存,如果生成的数组太大,内存就会溢出,有什么好的解决办法呢?生成器(generator)就横空出世了!
生成器会根据数组推算后续数值,一边循环一边计算,这样就不会占用大量空间.
那么问题来了,怎么创建生成器呢?非常简单,把列表生成器的中括号[]改为小括号就可以了()

>>> [x for x in range(10)][0, 1, 2, 3, 4, 5, 6, 7, 8, 9]>>> (x for x in range(10))<generator object <genexpr> at 0x7fea306baa50> #后面0x..是generator对象的内存地址.

通过上面的代码我们可以看到,生成器不会直接生成列表只是占用一点内存空间,若是需要输出数值有两个方法:一个是直接调用生成器的next()方法;二是通过for循环.
方法1:

>>> g = (x for x in range(10))>>> g.next()0>>> g.next()1>>> g.next()2>>> g.next()3>>> g.next()4>>> g.next()5>>> g.next()6>>> g.next()7>>> g.next()8>>> g.next()9>>> g.next()Traceback (most recent call last): #循环完毕跳出error  File "<stdin>", line 1, in <module>StopIteration

方法2:

>>> g = (x for x in range(10))>>> for x in g:...     print x... 0123456789>>> g.next()Traceback (most recent call last):  File "<stdin>", line 1, in <module>StopIteration#单个输出数值>>> g = (x for x in range(10))>>> l = []>>> for x in g:...     l.append(x)... >>> l[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]>>> g.next()Traceback (most recent call last):  File "<stdin>", line 1, in <module>StopIteration# 直接生成list>>> [x for x in range(10)][0, 1, 2, 3, 4, 5, 6, 7, 8, 9]>>> [x for x in range(10)][0, 1, 2, 3, 4, 5, 6, 7, 8, 9]# 列表生成器可以重复执行

通过比较方法1和方法2,方法2较方法1更方便,尤其是需要计算的数组比较大的时候.
另外细心的朋友估计能发现,不像列表生成器一样可以重复输出数组.
生成器是一种线性迭代是一次性的不能回头滴.撞了南墙也回不了头啊~只能说句say you error.

更加强大的generator

碰到更复杂的算法的时候,生成器的for循环就不能胜任了,这时候就要请出我们的函数君来解决问题.
以著名的斐波拉契数列(Fibonacci)来举例子吧.
**说明**Fibonacci数列是怎么一回事儿呢? 除了它的第一个和第二个数相等,任意一个数都可以由前面两个数相加得到.
1,1,2,3,5,8,13,21,….
普通文艺函数君:

>>> def fib(n):...     i,a,b = 0,0,1...     while i < n:...         print b # print b 要放在公式的前面执行....         a, b = b, a+b...         i = i + 1... >>> fib(5)11235>>> def fib(n):...     i,a,b = 0,0,1...     while i < n:...         print b...         a, b = b, a+b...         i = i + 1...         print 'new: ',b #添加一个放在后面的... >>> fib(5)1new:  11new:  22new:  33new:  55new:  8# 比较一下输出结果,得出的是(n+1)位的结果.

有可能有一部分同学已经迷糊了~这a , b, a+b,到底咋回事?
其实很好理解,我们先在Fibonacci数列前加个0,

> a     b    a+b # 第一次循环n=0的时候,a,b,a+b相对应的数值> 0,    1,    1,    2,    3,    5,    8>       a     b    a+b # 第二次循环n=1的时候,a,b,a+b相对应的数值

通过上面内容我们可以看到a,b,a+b是整体逐次步进的,再去对应下代码,是不是非常好理解~
理解之后我们发现,fib函数也是从第一个元素开始推算后续任意元素的,这个和generator的逻辑非常相似,所以我对普通文艺函数君稍微改造下就可能把他变为逗比generator函数君.
怎么改造呢?只要把fib函数里面的print b改为yield b就可以了!是高富帅还是吊丝就是一步之遥啊.

逗比generator函数君:

>>> def fib(n):...     i,a,b = 0,0,1...     while i < n:...         yield b...         a, b = b, a+b...         i = i + 1... >>> fib(5)<generator object fib at 0x7f29e33c9af0># 就是不输出,占坑不拉,你咬我啊.

怎么输出呢?

>>> f = fib(5)>>> f.next()1>>> f.next()1>>> f.next()2>>> f.next()3>>> f.next()5>>> f.next()Traceback (most recent call last):  File "<stdin>", line 1, in <module>StopIteration# next()方法单个输出>>> for n in fib(5):...     print n... 11235# for循环一次性输出

yieldprint有啥区别呢?可以这么理解yield,只能通过next()for循环来调用,在调用的过程中边运行边计算,当函数在循环过程中碰到yield就会输出一个数值.直到循环结束没有yield了然后返回一个error,当然只有使用next()方法的时候才会返回error,使用for循环的时候没有error返回.

0 0
原创粉丝点击