day18-列表生成式、迭代器

1、列表生成式,也叫列表推导式

即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式。
优点:构造简单,一行完成
缺点:不能排错,不能构建复杂的数据结构

1.1、循环模式[i for i in iterable]

 l1 = [i for i in range(1,101)]
 print(l1)

l2 = ['python第%s天'%i for i in range(1,11)]
print(l3)
结果:
[
'python第1天', 'python第2天', 'python第3天', 'python第4天', 'python第5天', 'python第6天', 'python第7天', 'python第8天', 'python第9天', 'python第10天']

10以内的数的平方

l3 = [i*i for i in range(11)]
print(l3)
结果:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

1.2、筛选模式[i for i in iterable if 判断式]

a = [i for i in range(1,100) if i%2==1]
print(list(a))或print(a)
结果:
[
1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95, 97, 99]

或一句话输出

print([i for i in range(1,100) if i%2==0])
结果:
[
2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98]

30以内能被3整除的数的平方

l4 = [i*i for i in range(31) if i%3 == 0]
print(l4)
结果:
[0,
9, 36, 81, 144, 225, 324, 441, 576, 729, 900]#

过滤掉非字符串类型和长度小于3的字符串,并将剩下的转成大写字母

lst = ['mike', 'tom', 'jack', 're', 'ab', 123]
l5 = [i.upper() for i in lst if isinstance(i, str) and len(i) >= 3]
print(l5)
结果:
['MIKE', 'TOM', 'JACK']

列出当前目录下的所有文件和目录名,可以通过一行代码实现:

import os 
print([d for d in os.listdir('.')]) # os.listdir可以列出文件和目录
['.emacs.d', '.ssh', '.Trash', 'Adlm', 'Applications', 'Desktop', 'Documents', 'Downloads', 'Library', 'Movies', 'Music', 'Pictures', 'Public', 'VirtualBox VMs', 'Workspace', 'XCode']

3、两层循环,可以生成全排列

print([m + n for m in 'ABC' for n in 'XYZ'])
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']

列表生成式也可以使用两个变量来生成list:

d = {'x': 'A', 'y': 'B', 'z': 'C' }
print([k + '=' + v for k, v in d.items()])
['y=B', 'x=A', 'z=C']

一句话生成九九乘法表

print ('
'.join([' '.join(['%s*%s=%-3s'%(y,x,x*y) for y in range(1,x+1)]) for x in range(1,10)]))

2、迭代器

1、可迭代对象

内部含有'__iter__'方法的数据就是可迭代对象
可迭代对象:list、str、tuple、set、dict、range()、文件句柄

print(dir(对象名))
如果打印结果里面有'__iter__',则这个对象就是可迭代对象

print('__iter__' in dir([1,2,3]))
True
如果结果为True,就表示对象是可迭代对象

2、迭代器
内部含有'__iter__'方法,并且含有'__next__'方法的对象就是迭代器
文件句柄就是一个迭代器

f1 = open('1.txt', encoding='utf-8'):
print('__iter__' in dir(f1))
True
print('__next__' in dir(f1))
True
l1 = [1,2,3,'a']
iter1 = iter(l1)
或
iter1 = l1.__iter__()

print(iter1.__next__())
print(iter1.__next__())
print(iter1.__next__())
print(iter1.__next__())

1
2
3
a
#迭代完了之后如果再取值,就会报错StopIteration

迭代器特点:
1、非常节省内存
2、满足惰性机制
3、一条路走到黑

利用while循环模拟for循环
1、将可迭代对象转化成迭代器
2、利用next进行取值
3、利用异常处理终止循环

l1 = [1,2,3,'a']
iter1 = l1.__iter__()
while 1:
    try:
        print(iter1.__next__())
    except StopIteration:
        break

3、生成器,自己用python代码写的迭代器

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

有两种方式:

1、函数式写法
2、生成器表达式

函数式写法,构建生成器
只要函数中出现yield,那么他就不是一个函数了,他是生成器函数

def func1():
    yield 3
    yield 4
func1()  #这样不会调用该函数

g = func1()
print(g.__next__()) #一个next对应一个yield的值
print(g.__next__()) #再一个next对应下一个yield的值

3
4

yield和return的区别

return 结束函数,给函数返回值
yield不会结束生成器函数,一个next对应一个yield进行取值

例子:如果循环打印一个列表,可以同时打印出来

def num1():
    for i in range(1,11):
        print('编号%s'%(i))
num1()


编号1
编号2
编号3
编号4
编号5
编号6
编号7
编号8
编号9
编号10

也可以不一次性打印出来,需要打印几个就先打印出几个

def num2():
    for i in range(1,11):
        yield ('编号%s'%(i))
g = num2() #这里需要将函数执行重新赋值给一个变量,不能用print(num2().__next__(),否则每次打印都是第一个值1
print(g.__next__())
#编号1
for i in range(3):
    print(g.__next__())
#编号2
#编号3
#编号4
for i in range(6):
    print(g.__next__())
#编号5
#编号6
#编号7
#编号8
#编号9
#编号10

4、生成器表达式

只需要将列表推导式用小括号表示即可生成一个生成器迭代器

g1 = (i*i for i in range(10000))
print(g1.__next__())
print(g1.__next__())
print(g1.__next__())
print(g1.__next__())
print(g1.__next__())
0
1
4
9

如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。
比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:
1, 1, 2, 3, 5, 8, 13, 21, 34, ...

def fib(max):
    n,a,b = 0,0,1
    while n<max:
        print(b)
        a,b = b, a+b
        n+=1

fib(6)
1
1
2
3
5
8
13

仔细观察,可以看出,fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。
也就是说,上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print(b)改为yield b就可以了:

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
    print('done')
g = fib(6)
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())
1
1
2
3
5

把函数改成generator后,如果用next()来获取下一个返回值有点麻烦,也可以直接使用for循环来迭代:

for n in fib(6):
    print(n)
1
1
2
3
5
8

例子:

杨辉三角定义如下:

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

把每一行看做一个list,试写一个generator,不断输出下一行的list:
def triangel(max):
    n,L=0,[1]               #定义一个list[1]
    while n<max:
        yield L             #打印出该list
        L=[1]+[L[x]+L[x+1] for x in range(len(L)-1)]+L[1] #计算下一行中间的值,两边再各添加一个[1]

#生成一个generator对象,然后通过for循环迭代输出每一行
a=triangel(10)
for i in a:
    print(i)

或者

def triangles(max):
    L=[1]
    i = 0
    while n<max:
        yield L                                  
        L.append(0)
        L = [L[i-1]+L[i] for i in range(len(L))]

a=triangel(10)
for i in a:
    print(i)

注:普通函数和generator生成器的区别:
1.普通函数调用直接返回结果,generator函数的调用,返回一个generator对象;
(调用generator时可以先创建一个对象,再用next()方法不断获得下一个返回值,但实际中通常用for循环实现)
2.generator在执行过程中,遇到yield就中断,下次又继续执行

原文地址:https://www.cnblogs.com/dxnui119/p/9910150.html