迭代器和生成器及生成器表达式、列表推导式

目录

1、迭代器

2、生成器

3、列表推导式

4、生成器表达式

5、各种推导式详解

6、小结


1、迭代器

迭代是Python最强大的功能之一,是访问集合元素的一种方式。

迭代器是一个可以记住遍历的位置的对象。

迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。

迭代器有两个基本的方法:__iter__() 和 __next__()

为何要有迭代器?什么是可迭代对象?什么是迭代器对象?

#1、为何要有迭代器?
对于序列类型:字符串、列表、元组,我们可以使用索引的方式迭代取出其包含的元素。
但对于字典、集合、文件等类型是没有索引的,若还想取出其内部包含的元素,则必须找出一种不依赖于索引的迭代方式,这就是迭代器

#2、什么是可迭代对象?
可迭代对象指的是内置有__iter__方法的对象,即obj.__iter__,如下
'hello'.__iter__
(1,2,3).__iter__
[1,2,3].__iter__
{'a':1}.__iter__
{'a','b'}.__iter__
open('a.txt').__iter__

#3、什么是迭代器对象?
可迭代对象执行obj.__iter__()得到的结果就是迭代器对象
而迭代器对象指的是即内置有__iter__又内置有__next__方法的对象

文件类型是迭代器对象
open('a.txt').__iter__()
open('a.txt').__next__()


#4、注意:
迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象 

迭代器对象的使用

dic={'a':1,'b':2,'c':3}
iter_dic=dic.__iter__() #得到迭代器对象,迭代器对象即有__iter__又有__next__,但是:迭代器.__iter__()得到的仍然是迭代器本身
iter_dic.__iter__() is iter_dic #True

print(iter_dic.__next__()) #等同于next(iter_dic)
print(iter_dic.__next__()) #等同于next(iter_dic)
print(iter_dic.__next__()) #等同于next(iter_dic)
print(iter_dic.__next__()) #抛出异常StopIteration,或者说结束标志


#基于for循环,我们可以完全不再依赖索引去取值了
dic={'a':1,'b':2,'c':3}
for k in dic:
    print(dic[k])

#for循环的工作原理
#1:执行in后对象的dic.__iter__()方法,得到一个迭代器对象iter_dic
#2: 执行next(iter_dic),将得到的值赋值给k,然后执行循环体代码
#3: 重复过程2,直到捕捉到异常StopIteration,结束循环

 迭代器的优缺点

#优点:
  - 提供一种统一的、不依赖于索引的迭代方式
  - 惰性计算,节省内存
#缺点:
  - 无法获取长度(只有在next完毕才知道到底有几个值)
  - 一次性的,只能往后走,不能往前退

2、生成器

什么是生成器

#只要函数内部包含有yield关键字,那么函数名()的到的结果就是生成器,并且不会执行函数内部代码

def func():
    print('a')
    yield 1
    print('b')
    yield 2
    print('c')
    yield 3
    print('d')

g=func()
print(g) #<generator object func at 0x0000000002184360>
特点:
调用函数的之后函数不执行,返回一个生成器
每次调用next方法的时候会取到一个值
直到取完最后一个,在执行next会报错
从生成器中取值的几个方法:
1、__next__()方法
2、for循环
3、数据类型的强制转换 : 占用内存

send的用法

def generator():
    print(123)
    content = yield 1
    print('========',content)
    print(456)
    yield 2
    print(789)
    yield 3

g = generator()
ret = g.__next__()
print('******',ret)
ret = g.send('hello')  #send()的效里跟__next__()一样
print('******',ret)

#send 获取下一个值的效果和next基本一致
#只是在获取下一个值的时候,给上一yield的位置传递一个数据
#使用send的注意事项
    # 第一次使用生成器的时候 是用next获取下一个值
    # 最后一个yield不能接受外部的值

send用法例子:计算移动平均值

def average():
    sum, count, avg = 0,0,0
    while True:
        num = yield avg
        sum = sum + num
        count += 1
        avg = sum/count

avg_a = average()
avg_a.__next__()
print(avg_a.send(10))
print(avg_a.send(10))
print(avg_a.send(20))
print(avg_a.send(20))

结果:
10.0
10.0
13.333333333333334
15.0

预激协程的装饰器 

def init(func):    #在调用被装饰生成器函数的时候首先用next激活生成器
    def inner(*args,**kwargs):
        ret = func(*args,**kwargs)
        ret.__next__()
        return ret
    return inner

@init
def average():
    sum, count, avg = 0,0,0
    while True:
        num = yield avg
        sum = sum + num
        count += 1
        avg = sum/count

avg_a = average()
# next(avg_a)   在装饰器中执行了next方法,无需调用__next__()
print(avg_a.send(10))
print(avg_a.send(10))
print(avg_a.send(20))
print(avg_a.send(20))
结果:
10.0
10.0
13.333333333333334
15.0

python3新增的yield from

def gen1():
    a = 'abcdef'
    b = '123456'
    for c in a:
        yield c
    for d in b:
        yield d

print(list(gen1()))  #打印变量a,b里面的元素

#使用yield from 打印
def gen2():
    a = 'abcdef'
    b = '123456'
    yield from a
    yield from b

print(list(gen2()))   #yield from 打印变量a,b里面的元素

结果:
['a', 'b', 'c', 'd', 'e', 'f', '1', '2', '3', '4', '5', '6']
['a', 'b', 'c', 'd', 'e', 'f', '1', '2', '3', '4', '5', '6']

3、列表推导式

#生成一个[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]的列表
lis = []
for i in range(10):
    lis.append(i)
print(lis)

#使用列表推导式
lis1 = [i for i in range(10)]
print(lis1)

lis2 = ['%d个苹果'%i for i in range(10)]
print(lis2)

结果:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
['0个苹果', '1个苹果', '2个苹果', '3个苹果', '4个苹果', '5个苹果', '6个苹果', '7个苹果', '8个苹果', '9个苹果']

4、生成器表达式

# 生成器表达式
g = (i for i in range(10))  #返回是一个生成器
print(g)  #打印是个生成器
for i in  g:
    print(i)

# 括号不一样
# 返回的值不一样 === 几乎不占用内存

#生成0-10的平方
g = (i*i for i in range(10))
for i in g:
    print(i)

5、各种推导式详解

推导式的套路

之前我们已经学习了最简单的列表推导式和生成器表达式。但是除此之外,其实还有字典推导式、集合推导式等等。

下面是一个以列表推导式为例的推导式详细格式,同样适用于其他推导式。

1.[每一个元素或者是和元素相关的操作 for 元素 in 可迭代数据类型] #遍历之后挨个处理

2.[满足条件的元素相关的操作 for 元素 in 可迭代数据类型 if 元素相关的条件] #筛选功能

列表推导式

#30以内所有能被3整除的数
ret = [i for i in range(30) if i%3 == 0]  #完整的列表推导式
print(ret)

#30以内所有能被3整除的数的平方
ret1 = [i*i for i in (1,2,3,4) if i%3 == 0]

print(ret1)

# 找到嵌套列表中名字含有两个‘e’的所有名字
names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
         ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
ret2 = [name for lst in names for name in lst if name.count('e') ==2]
print(ret2)

结果:
[0, 3, 6, 9, 12, 15, 18, 21, 24, 27]
[9]
['Jefferson', 'Wesley', 'Steven', 'Jennifer']

字典推导式

#例一:将一个字典的key和value对调
mcase = {'a': 10, 'b': 34}
#{10:'a' , 34:'b'}
mcase_frequency = {mcase[k]: k for k in mcase}
print(mcase_frequency)

#例二:合并大小写对应的value值,将k统一成小写
mcase = {'a': 10, 'b': 34, 'A': 7, 'Z': 3}
#{'a':10+7,'b':34,'z':3}
mcase_frequency = {k.lower(): mcase.get(k.lower(), 0) + mcase.get(k.upper(), 0) for k in mcase}
print(mcase_frequency)

结果:
{10: 'a', 34: 'b'}
{'a': 17, 'b': 34, 'z': 3}

集合推导式

#集合推导式,自带结果去重功能
squared = {x**2 for x in [1, -1, 2]}
print(squared)

结果:
{1, 4}

6、小结

可迭代对象:

  拥有__iter__方法

  特点:惰性运算

  例如:range(),str,list,tuple,dict,set

迭代器Iterator:

  拥有__iter__方法和__next__方法

  例如:iter(range()),iter(str),iter(list),iter(tuple),iter(dict),iter(set),reversed(list_o),map(func,list_o),filter(func,list_o),file_o

生成器Generator:

  本质:迭代器,所以拥有__iter__方法和__next__方法

  特点:惰性运算,开发者自定义

使用生成器的优点:

1.延迟计算,一次返回一个结果。也就是说,它不会一次生成所有的结果,这对于大数据量处理,将会非常有用。

#列表解析
sum([i for i in range(100000000)])#内存占用大,机器容易卡死
 
#生成器表达式
sum(i for i in range(100000000))#几乎不占内存

2.提高代码可读性 

猜你喜欢

转载自blog.csdn.net/qq_41922768/article/details/82458986