• python 生成器和递归


    生成器

    1.定义

    • 问题:python会把对象放到内存中,我们每次定义变量、列表等都会在内存中占用对应的地址块,所以当内存容量一定时,列表的容量会受到内存的限制,而且假如我们创建了一个包含200万个元素的列表,不仅会占用很大的地址空间,如果我们仅仅需要访问前面的几个元素,那么会造成后面的元素占用的空间都浪费了。基于这个问题,生成器就可以很好的解决。
    • 解决:生成器可以根据特定的算法,生成一个可迭代的对象,当我们调用此对象时,可以在循环过程中不断推算后续的元素,调用终止之后则不再循环,而后面的元素也就不在创建,这样很好的解决了上面的问题
    • 定义:生成器是这样一个函数,它记住上一次返回时在函数体中的位置。对生成器函数的第二次(或第 n 次)调用跳转至该函数中间,而上次调用的所有局部变量都保持不变。 生成器不仅“记住”了它数据状态;生成器还“记住”了它在流控制构造(在命令式编程中,这种构造不只是数据值)中的位置。
    • 生成器的特点: 生成器是一个函数,而且函数的参数都会保留。 迭代到下一次的调用时,所使用的参数都是第一次所保留下的,即是说,在整个所有函数调用的参数都是第一次所调用时保留的,而不是新创建的

    2.创建

    在函数中,如果使用了关键字yield时,此函数就是一个生成器,他的执行会和其他普通的函数有很多不同,函数返回的是一个对象,而不是一个结果值,如果需要得道结果值,需要调用next()函数
    如下:

    #定义一个生成器func
    def func():
        print(111)     #使用print打印每次循环的结果,便于查看  
        yield 1
        print(222)
        yield 2
        print(333)
        yield 3
    
    res = func()

    下面开始进行循环

    方法1:使用for循环

    for i in res:
    print(i)
    
    输出结果:
    111 #生成器第一次pring
    1 #yield返回的对象值
    222 #生成器第二次pring
    2 #yield返回的对象值
    333 #生成器第三次pring
    3 #yield返回的对象值


    方法2:使用next()迭代

    print("1".center(10,'*'))
    r1 = res.__next__()   #进入函数找到yield,并去到yield后面的数据
    print(r1)
    
    print("2".center(10,'*'))
    r2 = res.__next__()   #进入函数找到yield,并去到yield后面的数据
    print(r2)
    
    print("3".center(10,'*'))
    r3 = res.__next__()   #进入函数找到yield,并去到yield后面的数据
    print(r3)
    
    输出结果:
    ****1*****
    111
    1
    ****2*****
    222
    2
    ****3*****
    333
    3

    PS:for循环会自动调用next()方法,每当调用一次迭代器的next函数,生成器函数运行到yield之处,返回yield后面的值且在这个地方暂停,所有的状态都会被保持住,直到下次next函数被调用,或者碰到异常循环退出,所以如果一个生成器中一个yield被迭代之后,下次会读取下一个yield,所以for循环和next函数不能混用

    • 生成器应用实例:排列和组合
    #!/usr/bin/env python
    # -*- coding: UTF-8 -*-
    #pyversion:python3.5
    #owner:fuzj
    
    #组合生成器
    def perm(items, n = None):
        if n is None:
            n = len(items)
        for i in range(len(items)):
            v = items[i:i+1]          #使用字符串分片,取其中的元素
            if n==1:                #如果是第一次循环,则打印第一个字符和第二个字符
                yield v
            else:
                rest = items[:i] + items[i+1:]   #i+1表示接着上次运行的状态i的值进行切片,取后面的值字符
                for p in perm(rest, n-1):  #递归调用本生成器,进行循环,此就是之前描述的特定规则
                    yield v + p
    #排列生成器
    def comb(items, n = None):
        if n is None:
            n = len(items)
        else:
            for i in range(len(items)):
                v = items[i:i+1]      #使用字符串分片,取其中的元素
                if 1 == n:      #第一次循环,则打印第一个和第二个字符
                    yield v
                else:
                    rest = items[i+1:]   
                    for c in comb(rest, n-1):  #递归调用本生成器,进行循环,此就是之前描述的特定规则
                        yield v + c
    p = perm('abcd',2)
    c = comb('abcd',2)
    print('组合'.center(10,"="))
    for i in p:
        print(i)
    print('排列'.center(10,"="))
    for i in c:
        print(i)
    输出效果:
    
    ====组合====
    ab
    ac
    ad
    ba
    bc
    bd
    ca
    cb
    cd
    da
    db
    dc
    ====排列====
    ab
    ac
    ad
    bc
    bd
    cd

    递归

    递归的理解,可以从一个需求中进行慢慢剖析

    • 需求:计算1*2*3*....*1000的值

    这是一个简单计算叠乘的需求,如果没接触递归之前,可以使用函数来实现

    def func(arg):
        res = arg
        for i in range(1,arg):
            res *= 1 
        return res

    这个函数通过循环arg以内的数,再依次相乘最终返回结果,如果使用递归的方法,将更简单

    def fun(arg):
        if arg == 1:
            return arg
        return arg * fun(arg -1)
    • 通过此函数发现,函数最终return的是函数本身的表达式,如果计算5以内的阶乘,他的运行如下:
      1)fun(5)调用函数fun()函数,此时arg = 5,5>1,所以会return 5 * fun(4)
      2)fun(4)会调用fun()函数,此时arg=4,4>1,所以会return 4 * fun(3)
      3)fun(3)会调用fun()函数,此时arg=3,3>1,所以会return 3 * fun(2)
      4)fun(2)会调用fun()函数,此时arg=2,2>1,所以会return 2 * fun(1)
      5)fun(1)会调用fun()函数,此时arg=1,1=1,所以会return 1
      6)当fun(1)将值返回给fun(2)之后,此时fun(2)=fun(1)*2=2,继续往上返回给fun(3)
      7)当fun(2)将值返回给fun(3)之后,此时fun(3)=fun(2)*3=6,继续往上返回给fun(4)
      8)当fun(3)将值返回给fun(4)之后,此时fun(4)=fun(3)*4=24,继续往上返回给fun(5)
      9)当fun(4)将值返回给fun(5)之后,此时fun(5)=fun(4)*5=120,最终得出结果

    • 特点

    创建递归的条件

    1.一个基线条件:递归终止的条件,需递归开始的时候进行判断处理。
    2.一系列的规则:使对递归函数的每次调用都趋进于直至达到这个基线条件

    递归可以提高代码的可读性,但是运行效率较低。在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。

  • 相关阅读:
    JasperReport笔记
    关于iReport5.6.0无法正常启动或者闪退或者JDK8不兼容的解决方案
    sublime text3 3176激活
    直播技术之编码和封装
    直播技术之推流和传输
    Quic协议剖析
    Glide Picasso和Fresco的对比
    MVP架构学习
    反向打印链表
    重建二叉树
  • 原文地址:https://www.cnblogs.com/pycode/p/5574717.html
Copyright © 2020-2023  润新知