• 饮冰三年-人工智能-Python-16Python基础之迭代器、生成器、装饰器


    一:闭包


      1.1 什么是闭包  

        闭包就是内层函数, 对外层函数(非全局)的变量的引⽤.  
    def outerFun():
        name="Aaron"  # 外层函数outerFun 中的变量 name
        def innerFun():
            print(name) # 内层函数innerFun 引用了外层函数中的 name
        return innerFun
    
    f1= outerFun()
    f1()
    
    # 输出结果   Aaron 
    内层函数引用外层函数中的变量
    def outerFun():
        nameList=[]  # 外层函数outerFun 中的变量 nameList
        def innerFun(name):
            nameList.append(name) # 内层函数innerFun 引用了外层函数中的 nameList,并对其进行修改
            print(nameList)
        return innerFun
    
    f1= outerFun()
    f1("")
    f1("")
    f1("")
    f2= outerFun()
    f2("Tom")
    f1("")
    f2("Tony")
    '''
    ['赵']
    ['赵', '钱']
    ['赵', '钱', '孙']
    ['Tom']
    ['赵', '钱', '孙', '李']
    ['Tom', 'Tony']
    '''
    外部变量的变化
    • 结论:外部变量跟着各自的闭包走,可传递。但闭包之间不受影响
    def outfun(namelist=[]):
        def inner(name):
            namelist.append(name)
            print(namelist)
    
        return inner
    
    
    f = outfun()
    f("")
    f("")
    f("")
    f1 = outfun(namelist=["1"])
    f1("2")
    f1("3")
    f("")
    f1("4")
    """前方高能"""
    f2 = outfun()
    f2("A")
    修改接收到的可变对象参数

     1.2 如何判断函数是否是闭包 

      使⽤函数名.__closure__返回cell就是闭包。
    def outerFun():
        name="Aaron"  # 外层函数outerFun 中的变量 name
        def innerFun():
            print(name) # 内层函数innerFun 引用了外层函数中的 name
        return innerFun
    
    f1= outerFun()
    
    print(f1.__closure__)     #输出结果   (<cell at 0x00E7FE10: str object at 0x00E7FDE0>,)
    print(outerFun.__closure__) # 输出结果   None
    __closure__

     1.3 闭包的特点

    • 安全
    • 常驻内存

     1.4 闭包练习题  

    def my_func(*args):
        fs = []
        for i in range(3):
            def func():
                return i * i
            fs.append(func)
        return fs
    fs1, fs2, fs3 = my_func()
    print(type(my_func()))  #<class 'list'>
    print(fs1())            #4
    print(fs2())            #4
    print(fs3())            #4
    易错题

      python解释器开始执⾏之后, 就会在内存中开辟⼀个空间, 每当遇到⼀个变量的时候, 就把变量名和值之间的关系记录下来, 但是当遇到函数定义的时候, 解释器只是把函数名读入内存, 表⽰这个函数存在了, ⾄于函数内部的变量和逻辑, 解释器是不关⼼的. 也就是说⼀开始的时候函数只是加载进来, 仅此⽽已, 只有当函数被调⽤和访问的时候, 解释器才会根据函数内部声明的变量来进⾏开辟变量的内部空间. 随着函数执⾏完毕, 这些函数内部变量占⽤的空间也会随着函数执⾏完毕⽽被清空.  

    def my_func(*args):
        fs = []
        for i in range(3):
            def func(_i=i):
                return _i * _i
            fs.append(func)
        return fs
    fs1, fs2, fs3 = my_func()
    print(type(my_func()))  #<class 'list'>
    print(fs1())            #0
    print(fs2())            #1
    print(fs3())            #4
    正确写法

      结论:返回闭包中不要引用任何循环变量,或者后续会发生变化的变量

    二:迭代器


      1.1 迭代器协议    

        a:对象必须提供一个next方法

        b:执行方法要么返回迭代中的下一项,要么抛弃一个Stopiteration异常

        c:只能向后不能向前。

     1.2 可迭代对象

        实现了迭代器协议的对象。(对象内部定义一个__iter__()方法)

     1.3 使用迭代器访问对象

        检验可迭代对象向 Iterable: 可迭代对象. 内部包含__iter__()函数   Iterator: 迭代器. 内部包含__iter__() 同时包含__next__().

        常见的 str,list, tuple, dict, set,都是可以迭代的。

    from collections.abc import Iterable
    from collections.abc import Iterator
    l = [1,2,3]
    l_iter = l.__iter__()
    
    #检查是否是可迭代的对象
    print(isinstance(l,Iterable)) #True
    print(isinstance(l,Iterator)) #False
    print(isinstance(l_iter,Iterator)) #True
    print(isinstance(l_iter,Iterable)) #True
    检验可迭代对象
    st="hello";
    # 先将字符串对象转化为可迭代对象
    iterSt = st.__iter__()
    while True:
        try:
            print(iterSt.__next__())
        except StopIteration:
            # print('迭代完成')
            break
    迭代器之for循环模拟

    三:生成器


    1.1 什么是生成器?生成器的获取方法    

        使用了 yield 的函数被称为生成器,其实质就是迭代器.

        跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。 

      1.2 生成器特征    

        生成器函数:使用yield语句返回结果,在每个结果中间,挂起函数的状态,以便下次从此处继续执行。

        不用调用__iter__(),生成器生成的元素直接可调用__next__()方法。

        ⽣成器的惰性机制: 生成器只有在访问的时候才取值.   

      1.3 获取生成器:

        1:  通过生成器函数(含有yield的函数)    
    def fun():
        print(111)
        yield 222
    g = fun() #生成器
    print(g);  #输出结果:<generator object fun at 0x00EA5D30>
    生成器函数

        2: 通过生成器推导式来实现生成器    

          生成器表达式: (结果 for 变量 in 可迭代对象 if 条件筛选)
          gen = (i for i in range(1, 10) if i%2==0)  

      1.4 遍历生成器:

        1: 通过__next__()(一次只能获取一个)

        2:通过send()(一次只能获取一个,可以传递参数)

        3:通过for循环

        4:通过list()将其转换为列表   

    def fun():
        a = yield 1
        print("接收send传递的值", a)
        b = yield 2
        c = yield 3
        d = yield 4
        e = yield 5
    
    
    gen = fun()
    print("通过__next__()获取:", gen.__next__())
    hh=gen.send("12")
    print(hh)
    print("结论一:可以看出生成器是惰性机制,也可以叫做是懒加载。用一次给一次,节省内存!")
    for i in gen:
        print(i)
    print("结论二:由于生成器和迭代器类似一旦用过,不能再次使用!")
    print(list(gen))
    
    ''' 输出结果
    通过__next__()获取: 1
    接收send传递的值 12
    2
    结论一:可以看出生成器是惰性机制,也可以叫做是懒加载。用一次给一次,节省内存!
    3
    4
    5
    结论二:由于生成器和迭代器类似一旦用过,不能再次使用!
    []
     
    '''
    遍历生成器

        注意:send和__next__()的区别在于,send()可以传递参数。但是send可以给上一个yield的位置传递值, 不能给最后一个yield发送值。同时生成器某些元素用完以后就没有了。

      1.5. 其他推导式    

        列表推导式: [ 结果 for 变量 in 可迭代对象 if 条件 ]
               lst = [i for i in range(1, 10) if i%2==0] # [2, 4, 6, 8]
        字典推导式:{结果 for 变量 in 可迭代对象 if 条件}
               dic = {i-1:i for i in range(1, 10) if i%2==0} #{1: 2, 3: 4, 5: 6, 7: 8}
        集合推导式:{结果 for 变量 in 可迭代对象 if 条件}
              set = {i%2for i in range(1, 10) } #{0, 1}   

    1.6. 练习题 

        1.6.1 人口普查小练习  

    {"location":"北京",'count':100}
    {"location":"上海",'count':300}
    {"location":"广州",'count':200}
    {"location":"深圳",'count':400}
    人口普查.txt
    # 通过with as 的方式打开文件不用关闭
    def getPeople():
        with open("人口普查","r",encoding="utf-8") as f:
            for i in f:
                yield i
    g = getPeople();
    amount=sum(eval(i)["count"] for i in g)
    h=getPeople()
    for i in h:
        print(eval(i)["location"]+"的人数为:"+ str(eval(i)["count"]) +",占统计总人数的"+str(eval(i)["count"]/amount)+"%")
    
    '''
    北京的人数为:100,占统计总人数的0.1%
    上海的人数为:300,占统计总人数的0.3%
    广州的人数为:200,占统计总人数的0.2%
    深圳的人数为:400,占统计总人数的0.4%
    '''
    人口.py

        1.6.2 生产者消费者模式

    def consumer(name):
        print('我是【%s】,我准备开始吃包子了' %name)
        while True:
            baozi = yield
            print("【%s】很开心的吃掉了【%s】" %(name,baozi))
    
    def producer():
        c1=consumer('张三')
        c2=consumer("李四")
        c1.__next__()
        c2.__next__()
        for i in range(10):
            if i % 2 == 0:
                c1.send("包子 %s" %i )
            else:
                c2.send("包子 %s" %i)
    producer()
    
    '''
    我是【张三】,我准备开始吃包子了
    我是【李四】,我准备开始吃包子了
    【张三】很开心的吃掉了【包子 0】
    【李四】很开心的吃掉了【包子 1】
    【张三】很开心的吃掉了【包子 2】
    【李四】很开心的吃掉了【包子 3】
    【张三】很开心的吃掉了【包子 4】
    【李四】很开心的吃掉了【包子 5】
    【张三】很开心的吃掉了【包子 6】
    【李四】很开心的吃掉了【包子 7】
    【张三】很开心的吃掉了【包子 8】
    【李四】很开心的吃掉了【包子 9】
    '''
    生产者消费者模式
    # 猴子吃桃问题:猴子第一天摘下若干个桃子,当即吃了一半,还不瘾,又多吃了一个
    # 第二天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下
    # 的一半零一个。到第10天早上想再吃时,见只剩下一个桃子了。求第一天共摘了多少。
    
    
    # 方法一,递归调用
    def getNum(day,cou=1):
        cou =2 *(cou + 1) ;
        day-=1
        if day==1:
            return cou;
        return getNum(day, cou)
    print(getNum(10))
    # 方法二,递归调用
    def f(n):
        if n ==1:
            return 1
        return (f(n-1)+1)*2
    print(f(10))
    # 方法三,reduce函数
    s=1
    func=lambda x:(x+1)*2
    for x in range(9):
        s=func(s)
    print(s)
    猴子吃桃

        1.6.3 判断输出结果    

    def add(a, b):
        return a + b
    def test():
        for r_i in range(1,4):
            yield r_i
    g=test()
    
    for n in [2,10]:
        g=(add(n,i) for i in g)
    
    print(list(g))
    
    #[21, 22, 23]
    #根据生成器惰性机制,只有在list(g)的时候才开始调用,此时g的内容是什么?
    # 当n=2 时 g=(add(n,i) for i in g) 但是此时仍然没有执行
    # 当n=10 时 g=add(n,i) for i in (add(n,i) for i in g) ),开始执行。
    #   内存计算后得:g=add(n,i) for i in [11,12,13]
    #   再次计算可得:[21,22,23]
    生成器,惰性机制

    四:装饰器


    1.1 什么是装饰器?

    装饰器 = 高阶函数 + 函数嵌套 + 闭包

      本质就是函数,功能是为其他函数添加附加功能

      原则:对修改封闭,对扩展开放

        1:不能修改被修饰函数的源代码

        2:不能修改被修饰函数的调用方式   

    # 装饰器=高阶函数+函数嵌套+闭包
    # 高阶函数:传入参数或输出结果是一个函数
    # 函数嵌套:函数中定义函数
    import time
    def timmer(func):
        def wrapper():
            startTime= time.time()
            func();
            endTime= time.time()
            print("被测试函数一共运行:"+str(endTime-startTime)+"")
        return wrapper
    @timmer  #语法糖,相当于#test=timmer(test)
    def test():
        time.sleep(0.3);
        print("test函数运行完毕")
    test()
    装饰器-计算方法时间
    # 装饰器=高阶函数+函数嵌套+闭包
    # 高阶函数:传入参数或输出结果是一个函数
    # 函数嵌套:函数中定义函数
    import time
    def timmer(func):
        def wrapper(*args,**kwargs):
            startTime= time.time()
            res=func(*args,**kwargs);
            endTime= time.time()
            print("被测试函数一共运行:"+str(endTime-startTime)+"")
            return  res
        return wrapper
    @timmer  #语法糖,相当于#test=timmer(test)
    def test(a,b):
        return a+b
    
    print(test(100,200))
    装饰器-设置参数和返回值
    # 装饰器=高阶函数+函数嵌套+闭包
    # 高阶函数:传入参数或输出结果是一个函数
    # 函数嵌套:函数中定义函数
    import time
    # 添加一个参数,如果参数是n就打n折
    def disCount(n=1):
        def timmer(func):
            def wrapper(*args,**kwargs):
                startTime= time.time()
                res=func(*args,**kwargs)*n;
                endTime= time.time()
                print("今天是国庆节,每位客户打的折扣为:"+str(n*10))
                return  res
            return wrapper
        return timmer
    #@timmer  #语法糖,相当于#test=timmer(test)
    @disCount(n=0.9)
    def test(a,b):
        return a+b
    
    print(test(100,200))
    闭包,设置参数
  • 相关阅读:
    1017 A除以B (20分)**
    剑指 Offer 11. 旋转数组的最小数字(简单)
    剑指 Offer 04. 二维数组中的查找(中等)
    剑指 Offer 53
    剑指 Offer 53
    剑指 Offer 03. 数组中重复的数字(简单)
    剑指 Offer 58
    剑指 Offer 05. 替换空格(简单)
    执行npm install命令出错问题
    剑指 Offer 35. 复杂链表的复制(中等)
  • 原文地址:https://www.cnblogs.com/YK2012/p/9726739.html
Copyright © 2020-2023  润新知