列表生成式

列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式。
例如:

print(list(range(1,11)))
#打印结果
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

但是如果想要生成类似于x+x,x*x这种呢?

print([x*x for x in range(1,10)])
#打印结果
[1, 4, 9, 16, 25, 36, 49, 64, 81]

当然for循环是可以增加判断语句的!

print([x*x for x in range(1,10) if x%2 == 0])
#打印结果
[4, 16, 36, 64]

还可以生成两层循环的列表。

print([m+n for m in 'ABC' for n in '123'])
#打印结果
['A1', 'A2', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3']

使用这个技巧可以很容易写出一些很简洁的代码,例如查看当前目录下的目录:

print([d for d in os.listdir('.')])
#打印结果
['.idea', 'test.py']

最后将一个list中的字符串大写变小写,且不是字符串类型的需要过滤出去。

L = ['Hello', 123, 'ookkook', 33, 'ABC']

print([x.lower() for x in L if isinstance(x,str)])

#打印结果
['hello', 'ookkook', 'abc']

生成器

通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
它的创建很简单,相对于列表生成器的[]只需要改成()即可。
查看一下区别:

L = [x*x for x in range(1,5)]
G = (x*x for x in range(1,5))

print(L)
print(G)

#打印结果
[1, 4, 9, 16]
<generator object <genexpr> at 0x02557120>

创建L和G的区别仅在于最外层的[]和(),L是一个list,而G是一个generator。
可以使用next获取下一个变量:

print(next(G))
> 1

每次调用next时执行,但是遇到yield语句返回,然后再次执行从上次的yield开始 。
当然超出范围是要报错的,但是最好还是使用for循环简单一些。

def fun():
    print('step 1')
    yield 1
    print('step 2')
    yield 2
    print('step 3')
    yield 3

f = fun()
next(f)
next(f)
next(f)
print(f)

#打印结果
step 1
step 2
step 3
<generator object fun at 0x025A7330>

但是如果推断算法比较复杂的时候我们可能就需要借助于函数来实现了。

例如斐波那契数列,我们可以使用一个小技巧来实现。

def fib(n):
    num,a,b = 0,0,1
    while num < n:
        print(a)
        num, a, b = num + 1, b, a+b
    return 'end'

注意赋值语句:num, a, b = num + 1, b, a+b,这就相当于

tup = (num + 1, b, a+b) #一个tuple
num = tup[0]
a = tup[1]
b = tup[2]

我们来练习一下一个比较复杂的问题,就是生成杨辉三角。

1
        1   1
      1   2   1
    1   3   3   1
  1   4   6   4   1
1   5   10  10  5   1

当然这样肯定是不利于分析的,我们换个情况查看。

row
0    1
1    1 1
2    1 2 1
3    1 3 3 1
4    1 4 6 4 1
------------------
col  0 1 2 3 4

仔细分析一下:
1. col==0 的这一列上的元素总是 1 , 例如T(0,0),T(1,0),T(4,0)
2. col==row 的这一列上的元素总是 1, 例如 T(0,0),T(1,1),T(4,4)
3. T(row,col)上的元素等于 T(row-1,col-1)+T(row-1,col), 例如 T(4,3) == T(3,2)+T(3,3) 即 4 == 3 + 1 例如 T(4,2) == T(3,1)+T(3,2) 即 6 == 3 + 3

但是对于第三点, T(0,0) == T(-1,-1)+T(-1,0) ,我们分析一下。

我们再来看看第1行的情况, T(1,0) == T(0,-1)+T(0,0) ,T(0,0)是1,T(0,-1)不存在。 T(1,1) == T(0,0)+T(0,1) ,T(0,0)是1,T(0,1)不存在 根据第一点和第二点 我们知道T(1,0)和T(1,1)都是1,将已知量带入我们的式子. 1 = x+1 得x=0 1 = 1+x 得x=0 发现了没有,要想让这个算法进行下去,第0行元素始终要缺少两个0!

#在假想的情况下,第0行如果能像图中这样补上两个0,那么生成第1行的时候就轻松愉快了。
#上边我们分析过了生成第1行需要的T(0,-1)和T(0,1),现在已经有了!
r
0 [0 1 0]
1    1 1
2    1 2 1
3    1 3 3 1
4    1 4 6 4 1
------------------
c -1 0 1 2 3 4

接下来看代码:

def triangles():
    L = [1]
    while True:
        yield L
        L.append(0) #作者真的给上一行补了0,可是为什么只补了一个0?
        L = [L[i - 1] + L[i] for i in range(len(L))] #生成第1行

我们可以查看一下具体步骤

def triangles():
    L = [1]
    while True:
        print(L)
        yield L
        L.append(0) #补完0后L的状态 [1,0]
        L = [L[i - 1] + L[i] for i in range(len(L))] #生成第1行
#分析代码
#首先打印[1]
#然后append(0) L为[1,0] 然后根据公式来计算
#L = [L[-1]+L[0], L[0]+L[1]] = [1,1]
#第三行 L=[1,1,0]补充了一个0
#L = [L[-1]+L[0], L[0]+L[1], L[1]+L[2]] = [1,2,1]

我们可以可以看一下第二行的情况

#第一行是[1] 然后append了一个[0]
#然后生成第二行  
L = [1,0]
print(L[-1], L[-2])
> 0 1
L = [L[-1]+L[0], L[0]+L[1]] = [1,1]

迭代器

我们已经知道,可以直接作用于for循环的数据类型有以下几种:
一类是集合数据类型,如list、tuple、dict、set、str等。
一类是generator,包括生成器和带yield的generator function。
这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。
可以使用isinstance()判断一个对象是否是Iterable对象:

from collections import Iterable

print(isinstance([], Iterable))
print(isinstance({}, Iterable))
print(isinstance('abc', Iterable))

#打印结果
> True
> True
> True

而生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。
可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。
可以使用isinstance()判断一个对象是否是Iterator对象:

from collections import Iterator
print(isinstance((x for x in range(10)), Iterator))
print(isinstance([], Iterator))
print(isinstance({}, Iterator))
print(isinstance('abc', Iterator))
#打印结果
True
False
False
False

生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。
把list、dict、str等Iterable变成Iterator可以使用iter()函数:

print(isinstance(iter({}), Iterator))
print(isinstance(iter('abc'), Iterator))

你可能会问,为什么list、dict、str等数据类型不是Iterator?
这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算
Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。