zoukankan      html  css  js  c++  java
  • python语法生成器、迭代器、闭包、装饰器总结

    1、生成器

    生成器的创建方法:

    (1)通过列表生成式创建

    可以通过将列表生成式的[]改成()

    eg: 
    # 列表生成式
    L = [ x*2 for x in range(5)]  # L = [0, 2, 4, 6, 8]
    # 生成器
    G = ( x*2 for x in range(5))  # 此时的G是,<generator object <genexpr> at 0x7f626c132db0>
    

    创建列表生成式和生成器的区别只是最外层的()和[],列表生成式是一个列表,而生成器事宜个可迭代对象。生成器对象可以通过for语句遍历每个元素。

    for each in G:
    print(each)  # 打印结果是 0 2 4 6 8
    
    (2)通过函数来实现

    generator非常强大,如果推算的算法比较复杂,用类似的列表生成式的for循环无法实现的时候,可以通过函数来实现。

    以著名的斐波拉契数列(Fibonacci), 除第一个和第二个数外,任意一个数都可以由前两个数相加得到:1,1,2,3,5,8,13….

    斐波拉契数列用列表生成式不容易写出来,但是用函数把它打印出来很容易:

    def fib(times):
        n = 0
        a, b = 0, 1
        while n < times:
            yield b  # 这里用的yield将b的值返回到生成器中。
            a, b = b, a+b
            n += 1
        return 'done'
    
    f = fib(5)
    for each in f:
        print(each)
    

    上述例子中,在循环过程中不断调用yield,就不会中断函数,但是必须要指定一个结束循环的条件,不然会产生一个无限循环的数列出来。

    上述中,用for循环是拿不到return语句的返回值的,如果想拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:

    g = fib(5)
    while True:
        try:
            x = next(g)
            print('value: %d' % x)
        except StopIteration as e:
            print('生成器返回值:%s' % e.value)
            break
    

    这样就可以取到return的返回值了。

    (3)生成器的特点

    生成器是这样一个函数,它记住上一次返回时在函数体中的位置。对生成器函数的第二次(或第 n 次)调用跳转至该函数中间,而上次调用的所有局部变量都保持不变。

    生成器不仅“记住”了它数据状态;生成器还“记住”了它在流控制构造(在命令式编程中,这种构造不只是数据值)中的位置。

    生成器的特点:

    节约内存
    迭代到下一次的调用时,所使用的参数都是第一次所保留下的,即是说,在整个所有函数调用的参数都是第一次所调用时保留的,而不是新创建的

    2、迭代器

    迭代是访问集合的一种方式。迭代器是一个可以记住遍历的位置的对象,迭代器对象从第一个元素开始访问,直到所有的元素被访问完结束,迭代器只能往前不能后退。

    (1)可迭代对象

    可迭代对象可以直接用户for循环的数据类型:

    1. 第一类是集合数据类型:如list,tuple,set,dict,str等等。
    2. 第二类是generator,包括生成器和代yield的生成器函数。
    (2)判断是否为可迭代对象

    可以使用isinstance()函数来判断一个对象是否是可迭代对象。

    from collections import Iterable
    isinstance([], Iterable)  # 如果是可迭代对象则返回True,反之返回False。
    

    生成器不但可以作用于for循环,而且还可以被next()函数不断调用,知道抛出StopIteration错误,表示无法继续返回下一个值了。

    (3)迭代器

    可以被next()函数调用并不断返回下一个值得对象称为迭代器(Iterator)

    可以使用isinstance()函数来判断一个对象是否是迭代器对象。

    from collections import Iterator
    
    isinstance((x for x in range(10)), Iterator)  # 如果是迭代器则返回True,反之False
    
    (4)iter()函数

    生成器都是Iterator对象,但是list,dict,str虽然是Iterable,但是不是Iterator。如果想把list,dict,str等可迭代对象变成迭代器,可以使用iter()函数。

    isinstance 大专栏  python语法生成器、迭代器、闭包、装饰器总结(iter([]), Iterator)  # 返回值是True,证明iter([])是迭代器。
    
    (5)总结
    1. 凡是可以用for循环的对象,都是可迭代对象类型(iterable)
    2. 凡是可用于next()函数的对象,都是iterator(迭代器)类型。
    3. 集合数据类型,如list,dict,str等是可迭代对象,但不是迭代器,可以通过iter()函数获得一个迭代器对象。

    3、闭包

    闭包是在函数内部再定义一个函数,并且这个函数用到了外部函数的变量,就将这个函数以及用到的一些变量称之为闭包。简言之,内部函数对外部函数作用域里变量的引用(非全局变量),则称内部函数为闭包。

    闭包示例:
    def line_conf(a, b):
        def line(x):
            return a*x + b
        return line
    
    line1 = line_conf(1, 1)
    line2 = line_conf(4, 5)
    print(line1(5))
    print(line2(5))
    

    上例中,函数line与变量a, b构成闭包。在创建闭包的时候,我们通过line_conf的参数a, b说明了这两个变量的取值,这样,我们就确定了函数的最终形式(y = x + 1 和 y = 4x + 5)。我们只要变换参数a, b就可以得到不同的曲线表达函数,由此,闭包也具有提高代码可重复用性的作用。

    如果没有闭包,我们需要每次创建直线函数的时候同时说明a, b, x。这样,我们需要更多的参数传递,也减少了代码的可移植性。

    闭包总结:
    1. 闭包优化了变量,原来需要类对象完成的工作,闭包也可以完成。
    2. 由于闭包引用的外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存。

    4、装饰器

    装饰器其实就是一个闭包,把一个函数当做参数然后返回一个替代的函数。这个替代的函数可以增添新的功能。

    (1)装饰器(decorator)功能
    1. 引入日志
    2. 函数执行时间统计
    3. 执行函数前的预处理
    4. 执行函数后的清理功能
    5. 权限校验等场景
    6. 缓存
    (2)装饰器示例
    1. 无参数函数
    from time import sleep, ctime
    
    def timefun(func):
        def wrappedfunc():
            print('%s is called at %s'%(func.__name__, ctime())
            func()
        return wrappedfunc
    
    @timefunn
    def foo():
        print('I am foo')
    
    foo()
    sleep(2)
    foo()
    
    2. 被修饰的函数有参数
    from time import ctime, sleep
    
    def timefunc(func):
        def wrappedfunc(a, b):
            print('%s is called at %s'%(func.__name, ctime())
            print(a, b)
            func(a, b)
        return wrappedfunc
    
    @timefunc
    def foo(a, b)
        print(a+b)
    
    foo(3, 5)
    sloop(2)
    foo(2, 4)
    
    3. 被修饰函数带有不定长参数
    from time import ctime, sleep
    
    def timefunc(func):
        def wrappedfunc(*args, **kwargs):
            print('%s is called at %s'%(func.__name__, ctime())
            func(*args, **kwargs)
        return wrappedfunc
    
    @timefunc
    def foo(a, b, c):
        print(a+b+c)
    
    foo(3,5,2)
    sleep(2)
    foo(2, 4, 9)
    
    4.装饰器中带有return
    from time import ctime, sleep
    
    def timefunc(func):
        def wrappedfunc():
            print('%s called at %s'%(func.__name__, ctime())
            func()
        return wrappedfunc
    
    @timefunc
    def foo():
        print('i am foo')
    
    @timefunc
    def getInfo():
        return '----hahahah-----'
    
    foo()
    sleep(2)
    foo()
    
    print(getInfo())
    
    #执行结果是
    foo called at ....
    I am foo
    foo called at .....
    I am foo 
    getInfo called at ...
    None
    

    有结果可见,带有return的修饰器,如果还是按照上述的写法,则不会返回值。如果把修饰器函数中的func(),改为return func(),则print(getInfo())的执行结果是:
    getInfo called at …
    —–hahahah—–

    这样return能正确返回。

  • 相关阅读:
    【Linux系列汇总】小白博主的嵌入式Linux实战快速进阶之路(持续更新)
    【matlab系列汇总】小白博主的matlab学习实战快速进阶之路(持续更新)
    【FreeRTOS学习01】CubeIDE快速整合FreeRTOS创建第一个任务
    STM32F767ZI NUCLEO144 基于CubeIDE快速开发入门指南
    【matlab 基础篇 01】快速开始第一个程序(详细图文+文末资源)
    Linux 通过终端命令行切换系统语言
    ubuntu 1604升级到ubuntu 1804无法忽视的细节问题(亲测有效)
    假如用王者荣耀的方式学习webpack
    小程序第三方框架对比 ( wepy / mpvue / taro )
    我所理解的前端
  • 原文地址:https://www.cnblogs.com/lijianming180/p/12286200.html
Copyright © 2011-2022 走看看