第十三天

第一部分 迭代器

"""
迭代器
"""
li=[1,2,3,4]
tu=(1,2,3,4)
s="abc"
b=b"def"
d={"a":1,"b":2}
se={1,2,3}
for i in li:
    pass
for i in tu:
    pass
for i in s:
    pass
for i in b:
    pass
for i in d:
    pass
for i in se:
    pass
# for i in 1:
#     pass

  


1. 可迭代对象
# 能够放在for循环中进行遍历的类型,就是可迭代类型,产生的对象就是可迭代对象
# 可迭代对象类型,在python collections.abc.Iterable,包括__iter__方法。所有的可迭代对象。
# 必须都实现__iter__方法。返回的是迭代器。
# 学过的list,tuple等可迭代对象都是继承了这个父类Iterable,实现__iter__

from collections.abc import Iterable
print(issubclass(list,Iterable))
print(issubclass(tuple,Iterable))
print(issubclass(str,Iterable))
print(issubclass(bytes,Iterable))
print(issubclass(dict,Iterable))
print(issubclass(set,Iterable))
print(issubclass(int,Iterable))

  

# 说明:可迭代对象有一些没有实现__iter__方法,是特殊的,而是实现的__getiterm__方法

2. 迭代器
# 迭代器是迭代对象的特殊类型,迭代器是迭代对象的子类
from collections.abc import Iterator
print(issubclass(Iterator,Iterable))

# 在迭代器中两个重要方法 __next__ __iter__
# __next__方法:能够返回迭代器下一个元素
li=[1,2,3] # li是迭代对象
# __iter__方法:能够返回迭代器
# li.__iter__() iter(li)在调用上等价,但是建议用后一种方法
# it=iter(li)
# it 是迭代器,迭代器中包含了所有的元素
it=iter(li)
# it.__next__() next(it)在调用上等价,建议用后一种
print(it.__next__())
print(next(it))
print(next(it))
print(next(it)) # 当迭代器中的元素已经全部被next获得之后,会报StopIteration的异常
# 迭代器产生一次,只要遍历过,当前的迭代器中的元素就没有了。

# for 循环是怎么做到可以一直遍历迭代对象的呢?
# 是因为每次for的时候,都会新创建一个迭代器,进行迭代
li1=[1,2,3,4,5]
# it=li1.__iter__()
# 获得当前迭代对象的迭代器(这个迭代器一次只产生一个)
it=iter(li1)
while True:
try:
item=next(it)
except StopIteration:
del it
break
# 注意:
# 1. 分清迭代对象是什么?迭代器是什么?
# Iterable 迭代对象,我们学过的所有的list,tuple都是Iterable的子类
# 如果新创建一个列表,是一个对象,是list的对象,list是Iterable的子类

# 2. 他们之间的关系
# Iterator,是迭代对象的子类

# 3. 迭代对象下有什么方法?干什么的?
# # Iterable下有__iter__方法,注意,调用的时候,可以直接使用__iter__,建议使用iter(迭代对象)
# iter 方法能够返回一个迭代器。

# 4. 迭代器下有什么方法?干什么的?
# Iterator 有__next__:能够找到迭代器中的下一个对象。
# 还有 __iter__:return self。 为了扩展使用

# 注意,迭代器在遍历一次之后,迭代器的元素就没了,如果需要再次遍历,需要重新使用迭代对象
# 调用迭代对象下的iter方法创建新的迭代器。

3. 自定义迭代对象和迭代器。
# 符合如下规则:
迭代对象: 里面实现__iter__方法。
迭代器: 里面实现__iter__和__next__方法
class MyIterable:
def __iter__(self):
pass
class MyIterator:
def __iter__(self):
pass
def __next__(self):
pass
# 只要定义的迭代对象,实现了__iter__方法,那么它就是迭代对象,就是Iterable的子类,不需要显示继承
# 只要定义了迭代器,实现了__next__ 和__iter__,那么它就是迭代器,就是iterator的子类,不需要显示继承

m=MyIterable()
print(isinstance(m,Iterable))
print(issubclass(MyIterable,Iterable))
n=MyIterator
print(isinstance(n,Iterator))
print(issubclass(MyIterator,Iterator))

class MyIterable1:
    def __iter__(self):
        self.li=[1,2,3,"a"]
        return
class MyIterator1:
    def __init__(self,li):
        self.li=li
        self.index=0
    def __iter__(self):
        pass
    def __next__(self):
        if self.index<len(self.li):
            s=self.li[self.index]
            self.index+=1
            return s
        else:
            # 当索引大于长度
            raise StopIteration
m=MyIterable()
m1=iter(m)
print(m1.__next__())
print(next(m1))

  


# 为什么list 或者tuple这些可迭代的数据类型被设计成“迭代对象”,而不是直接设计成“迭代器"?
# 迭代器


# 写一个迭代器,实现一个倒数的迭代器



4. 迭代器的缺点
# (1) 必须要去实现iter方法和next方法
# (2) 如果不是用for 循环,那么无返回值的时候,需要捕捉StpIteration的异常
# (3) 一次性全部迭代,结果未必是一下子全部使用,当数量级太大的时候,会占用太多的内存

# 解决方法-------生成器


第二部分 生成器

"""
生成器
在python2.5之后出现的,生成器返回一个可迭代的对象,但是不是立即返回所有的值,而是一个一个的生成值
懒加载:延迟加载,按需加载。
生成器,其实就是给一个函数,生成一个懒加载的迭代器。
"""
# 将列表中的每一个元素的数,求平方
li=[]
for i in range(1,101):
li.append(i**2)
print(li)

print([i**2 for i in range(1,101)])

# 生成器的实现有两种方式:一种生成器表达式,另一种是生成器函数
1. 使用生成器表达式
linew=(i**2 for i in range(1,101))
# linew是一个生成器对象
print(linew)
# 因为生成器底层是迭代器,使用for 循环,可以触发生成器产生每一个元素
# for i in linew:
# print(i)
# 生成器就是迭代器,遍历完一次之后就没有东西了
print(next(linew))

2. 生成器函数
生成器函数就看有没有yield
# 定义一个普通函数
# def f():
# for i in range(1,101):
# print(i)
# f()

# 生成器函数
def f():
print("生成器函数开始执行")
for i in range(1,101):
yield i
print(i,"暂停之后继续执行")
a=f()
print(a)
a.__next__()
next(a)
next(a)
for i in a:
print(i)

# 练习:使用正常函数和生成器函数写出斐波那契数列
def f():
print("程序开始执行")
a=0
b=1
for i in range(100):
print("yield之前")
yield b
print("yield之后继续执行")
a,b=b,a+b
g=f()
print(next(g))
print(next(g))
print(next(g))
print(next(g))

# 说明: 1. 生成器函数,在被调用的时候,只会产生一个生成器对象(本质迭代器对象)
# 2. 不会运行函数中的任何一行代码
# 3. 当使用next(生成器对象),才触发函数执行,运行到yield暂停
# 4. 当继续调用next函数时,从刚才暂停的位置,继续向后执行

"""
生成器函数和普通函数的区别
1. 生成器函数会包含一个或者多个yield
2. 当生成器函数运行到yield,暂停,相当于把控制权交给调用者
"""

3. yield表达式。
# 在生成器函数除了可以使用yield产生一个值以外,其实,同时yield表达式本身还具有一个值,
# 不是产出的值,而是使用send方法传递进去的值
# send(值)
def fun():
for i in range(100):
value=yield i
print(value)
g=f()
print(g.send(None))
print(g.send("send传入的值"))
print(next(g))
print(g.send("这是send传进来的值"))
print(g.send(None))

# send 和 next 他们都可以调用生成器对象产生的元素
# 调用next函数,相当于传入值None,next函数的返回值是yield生成的内容
# 调用send函数,可以传入yield表达式的值(生成器函数中value值,value=yield i)
# send函数的返回值,也是yield生成的内容
# 第一次调用next唤醒生成器对象,跟使用send(None)效果一样
# 第一次调用只能使用send(None),分析原因,停在yield,还没有对左侧的value进行赋值

# 需求:使用生成器产生一系列的值,比如,1,2,3..到5时,返回给客户端,说太大了,变小点
# 生成器在继续产生4,3,2,1,到1的时候,客户端说太小了,变大点,生成器继续产生2,3,4,5
第三部分 闭包
"""
闭包:
在内部函数中,访问外部函数的变量,在外部函数中,直接返回内部函数引发调用
(1) 有嵌套函数
(2) 内部函数可以访问到外部函数的变量
(3) 外部函数返回内部函数的名字
"""
def abc():
    print("已经存在的函数")
def outer(a):
    x=1
    def inner():
        print(x)
        print(a)
        print("inner函数")
    return inner

inner=outer("abc")
print(inner.__closure__)
inner()

  

# 闭包的作用:
# 1. 当函数执行结束时,内部函数依然可以引用到外部函数中定义的变量,下一次再调用内部函数时
# 依然可以调用到外部函数中变量值。
# 2. 内部函数的名称处于局部命名空间,不会对外部全局命名空间造成影响,即使函数名相同也没问题

# 实现bill第几天上班
def on_duty(name):
    times=0
    times+=1
    print("这是{}第{}次上班".format(name,times))
on_duty("bill")
on_duty("bill")

# 闭包
def on_duty(name):
    times=0
    def inner():
        nonlocal times
        times+=1
        print("这是{}第{}次上班".format(name,times))
    return inner

duty=on_duty("bill")
duty()
duty()
duty()

 

# 通过类也能实现闭包
class Onduty:
    def __init__(self,name):
        self.name=name
        self.times=0
    def __call__(self, *args, **kwargs):
        self.times+=1
        print("这是{}第{}次上班".format(self.name,self.times))
duty=Onduty("bill")
duty()
duty()

  





















原文地址:https://www.cnblogs.com/ztx695911088/p/9108961.html