Python_基础_(迭代器,生成器)

一,迭代器

1,迭代器协议:对象必须具有一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopInteration异常,以来终止迭代(只能往后走,不能向前进)

 2,可迭代对象:实现迭代器协议的对象(再对象的内部定义一个__iter__()方法)

问题:for循环的本质就是去遵循迭代器协议去访问对象,for循环可以遍历(字符串,列表,元组,字典,集合,文件对象),那么这些数据类型肯定也是可迭代对象?但定义一个列表L = [1,2,3],中没有L.next方法

解答:(字符串,列表,元组,字典,集合,文件对象)都不是可迭代对象,只是在进行for循环时,调用了它们内部的__iter__方法,将它们编程可迭代对象

# __iter__()  __next__()  方法

test = "hello"
iter_test = test.__iter__()     # 再字符串test的内部具有__iter__方法
print(iter_test)                # 一个可迭代对象<str_iterator object at 0x00000226F70325F8>
print(iter_test.__next__())     # 具有 __next__()  方法,输出 h

# for循环与__iter__()方法

l = [1,2,3,4,5]
for i in l:    # 这一步相当执行了 l.__iter__() 将其转为可迭代对象
    print(i)

# 输出
1
2
3
4
5
## 注:因为可迭代对象在没有下一个子元素时,会引起StopIteration异常,但在进行for循环是,循结束时不会引起异常,
## 因为for循环捕捉到了该异常,停止再次进行循环

# 注:非序列类型不能利用while进行遍历 字典,集合,文件对象(就是无下标),可以利用for循环来遍历

# for 循环就是基于迭代器提供了一个统一的可以遍历所有对象的方法,在遍历之前需要调用对象的__iter__方法将其转换成一个迭代器

# 用可迭代的方式除去列表中元素的值

l_list = [1,2,3]
list_iter = l_list.__iter__()    # 将其变为可迭代对象
print(list_iter.__next__())    # 取得第一个对象1
print(list_iter.__next__())    # 取得第二个对象2
print(list_iter.__next__())    # 取得第三个对象3
# print(list_iter.__next__()) # 会有报错

# 内置函数 next()用法

l = [1,2,3,4,5]
iter_l = l.__iter__()
print(next(iter_l))    # 输出第一个值1 
            # next()方法为内置函数,调用next()相当于iter_1.__next__()

。。。

二,生成器

生成器:可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其它的数据类型需要调用__iter__()  方法),所以生成器为可迭代对象 

## Python中的生成器

1.生成器函数:定义一个普通的函数,但是函数不利用return返回结果,而是利用  yield  语句来返回结果

2.yield 一次只返回一个结果,在每个结果的中间,挂起函数的状态,以便下次执行

3.生成器表达式:与列表的推导类似,但是,生成器按需产生结果的一个对象,而不是一次构建一个结果列表

4.生成器的优点:使用生成器对延迟操作提供支持,所谓的延操作指再需要的时候才产生结果,而不是立即产生结果

# 生成器小结:

1,可迭代对象

2,实现延迟计算,节省内存

3,生成器和其它的数据类型一样,都是实现了迭代器协议,但生成器附加了一个延迟计算省内存的,其它的可迭代对象没有

## 生成器函数

def test():
    yield 1  # 返回一个值 可以填入各种形式的值
    yield 2
    yield 3


g = test()  # 获得生成器,但生成器未运行
print(g)    # <generator object test at 0x0000025BB0032480>
print(g.__next__())  # 输出:1     执行生成器
print(g.__next__())  # 输出:2
print(g.__next__())  # 输出:3     当返回多个值是这样则不会报错
print(g.__next__())  # 报错 抛出异常:StopIteration

# 生成器只能遍历一次

def test():
    for i in range(4)
    yield i
t = test()    # 获得生成器,但生成器未运行

for i in t:
    print(i)    # 对生成器进行迭代

for i in t:
    print(i)    # 输出为空,因为生成器最多只能迭代一次 

# 生成器例子

# 员工工资表.txt
{"position":"老板","salary":"1"}
{"position":"秘书","salary":"10000"}
{"position":"文员","salary":"10000"}
{"position":"工程师","salary":"100000"}
{"position":"经理","salary":"10000"}

def get_salary(filename):
    with open(filename) as f:
        for line in f:
            s = eval(line)
            yield s["salary"]
            
res = get_salary("员工工资表.txt")

all_salary = sum(res)
for i in res:
    print(p)
    
# 当执行for循环时,将不会有任何的输出,因为生成器只执行一次,在执行sum语句时。就遍历了生成器,当在for循环语句时,再次遍历生成器时,将不会取到任何值

....

三,三元表达式
name = "henry"
res = "hello" if name == "henry" else "hi"
res = print("名字为henry") if name == "henry" else print("名字不为henry")
# 当名字为henry时 执行"hello"
# 当名字不为henry时 执行hi
# res为取得的返回值

 ....

四,列表解析
hot_spicy_pot = []
for i in range(10):
    hot_spicy_pot.append("吃%s份麻辣香锅" %i)
print(hot_spicy_pot)
# ['吃0份麻辣香锅', '吃1份麻辣香锅', '吃2份麻辣香锅', '吃3份麻辣香锅', '吃4份麻辣香锅', '吃5份麻辣香锅', '吃6份麻辣香锅', '吃7份麻辣香锅', '吃8份麻辣香锅', '吃9份麻辣香锅']

# 三元表达式
hot_spicy_pot = []
[hot_spicy_pot.append("吃%s份麻辣香锅" %i) for i in range(10)]
print(hot_spicy_pot)
# ['吃0份麻辣香锅', '吃1份麻辣香锅', '吃2份麻辣香锅', '吃3份麻辣香锅', '吃4份麻辣香锅', '吃5份麻辣香锅', '吃6份麻辣香锅', '吃7份麻辣香锅', '吃8份麻辣香锅', '吃9份麻辣香锅']

hot_spicy_pot = []
[hot_spicy_pot.append("吃%s份麻辣香锅" %i) for i in range(10) if i > 5]
print(hot_spicy_pot)
# ['吃6份麻辣香锅', '吃7份麻辣香锅', '吃8份麻辣香锅', '吃9份麻辣香锅']

# 如果当处理的数据较大时,若将所处理的数据全部放于内存中(例如构建一个较大的列表时),会比较占内存

### 所以就有了生成器表达式

# 生成器表达式(将列表解析中的中括号改成小括号)

列表解析:["吃%s份麻辣香锅" %i for i in range(10) ]
生成器表达式:("吃%s份麻辣香锅" %i for i in range(10))
# 列表解析
hot_spicy_pot = []
res = [hot_spicy_pot.append("吃%s份麻辣香锅" %i) for i in range(10)]
print(hot_spicy_pot)
print(res)      # 无意义 [None, None, None, None, None, None, None, None, None, None]

# 生成器表达式
hot_spicy_pot = []
res = ("吃%s份麻辣香锅" %i for i in range(10))
print(hot_spicy_pot)    # []
print(res)          # <generator object <genexpr> at 0x0000018945032480>  可迭代对象
print(res.__next__())   # 吃0份麻辣香锅
print(next(res))        # 吃1份麻辣香锅

# 总结:

1.列表解析和生成器表达式都是一种遍历的编程方式,但生成器表达式更节省内存

# 例:当计算一个较大的列表时,需要将其先加载到内存中,所以会占用较大的内存,可以用下列的方法解决

sun(i for i in range(10000000000000000000))

。。。。

五,程序的并发操作
# send(self,value)方法

def test():
    print("开始啦")
    first = yield 1
    print("第一次",first)  # 输出的为 None
    yield 2
    print("第二次")


t = test()
res = t.__next__()
t.send(None)    # value可以为任何值    
# 注:执行完上一次的__next__(),当前的执行位置位于yield后,当执行send(None)时,将None的值赋值给first
##注:yield相当于return控制的函数的返回值
# x = yield的另一个特性,接收传过来的值,赋值给x

 # 吃包子模拟单线程

def consumer(name):
    print("我是[%s],我准备开始吃包子了"%name)
    while True:
        baozi = yield
        print("%s很开心的把{%s}吃掉了"%(name,baozi))

def producer():
    c1 = consumer("henry")
    c1.__next__()
    for i in range(10)
        c1.send("韭菜包子馅")

。。。。

六,总结

猜你喜欢

转载自www.cnblogs.com/Doaoao/p/10133602.html