• python之路6-迭代器、生成器、装饰器


    1、迭代器&生成器

    列表生成式

    现在有个需求,列表[1,2,3,4,5,6,7,,8,9],要求把列表里的每个值加1,如何实现?

    方法一:

    list = [1,2,3,4,5,6,7,8,9]
    
    for index,i in enumerate(list):
        list[index] = i + 1
    
    print(list)
    运行结果:
    [2, 3, 4, 5, 6, 7, 8, 9, 10]

     方法二:

    list = [1,2,3,4,5,6,7,8,9]
    #map()是 Python 内置的高阶函数,它接收一个函数 f 和一个 列表list,并通过把函数 f 依次作用在 list 的每个元素上,
    # 得到一个新的 list 并返回
    a = map(lambda x:x+1,list)
    for i in a:
        print(i)
    运行结果:
    [2, 3, 4, 5, 6, 7, 8, 9, 10]

     其实,还有一种写法

    list = [1,2,3,4,5,6,7,8,9]
    a = [x+1 for x in list]
    print(a)
    运行结果:
    [2, 3, 4, 5, 6, 7, 8, 9, 10]

     这就叫做列表生成式

     生成器

    通过列表生成式,我们可以创建一个列表,但是,受到内存限制,列表容量是有限的。而且,创建一个包含100百万元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间就白白浪费了。所以,如果列表元素可以按照某种算法推算出来,那我们是否可在循环过程中不断推算出后面的元素呢?这样就不需创建完整的列表,从而节省大量空间。在python中,这种一边循环一边计算的机制,称为生成器:generator。创建一个generator,有很多中方法,第一种方法很简单,只要把列表生成式中的[]改成(),就创建了一个generator。

    a = [x+1 for x in range(10)]
    print(a)
    a = (x+1 for x in range(10))
    print(a)
    运行结果:
    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    <generator object <genexpr> at 0x0000017258EF2830>
    

     generator要打印出来,需要通过next()函数获得generator的返回值

    print(next(a))
    1
    print(next(a))
    2
    

    generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

    上面这种不断调用next(g)实在是太变态了,正确的方法是使用for循环,因为generator也是可迭代对象:

    for i in a:
        print(i)
    

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

    比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:

    1, 1, 2, 3, 5, 8, 13, 21, 34, ..

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

    def fib(max):
        n,a,b=0,0,1
        while n<max:
            print(b)
            a,b=b,a+b
            n +=1
        return 'done'
    
    fib(10)
    

      

    仔细观察,可以看出,fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。

    也就是说,上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print(b)改为yield b就可以了:

    def fib(max):
        n,a,b=0,0,1
        while n<max:
            yield b
            a,b=b,a+b
            n +=1
        return 'done'
    

      这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。yield相当于一个断点

    f = fib(10)
    print(f.__next__())
    print(f.__next__())
    print("干点别的事")
    print(f.__next__())
    print(f.__next__())
    运行结果:
    1
    1
    干点别的事
    2
    3
    

      同样的,把函数改成generator后,我们基本上从来不会用next()来获取下一个返回值,而是直接使用for循环来迭代:

    for i in fib(10):
        print(i)
    

      迭代器

    我们已经知道,可以直接作用于for循环的数据类型有以下几种:

    一类是集合数据类型,如listtupledictsetstr等;

    一类是generator,包括生成器和带yield的generator function。

    这些可以直接作用于for循环的对象统称为可迭代对象:Iterable

    可以使用isinstance()判断一个对象是否是Iterable对象:

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

    个人理解:生成器就是迭代器

    3、装饰器

    软件开发原则:开放-封闭,不能改变原代码的调用方式。

    • 封闭:已实现的功能代码块不应该被修改
    • 开放:对现有功能的扩展开放

    闭包函数:python中的闭包从表现形式上定义(解释)为:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。

     1 import time
     2 def timer(func):
     3     def wrapper(*args,**kwargs):
     4         start_time=time.time()
     5         res=func(*args,**kwargs)#执行index原始函数
     6         stop_time = time.time()
     7         print('run time is %s' %(stop_time-start_time))
     8         return res
     9     return wrapper
    10 
    11 @timer #index=timer(index)
    12 def index(msg):
    13     print('in the index: ',msg)
    14     return 1
    15 res=index('hello world') #wrapper('hello world')
    16 print(res)
    17 # 装饰器执行流程:
    18 # 1、导入time模块
    19 # 2、定义timer函数,函数名作为变量名赋值给timer函数
    20 # 3、@timer index赋值给timer函数
    21 # 4、定义闭包函数wrapper,直接返回wrapper
    22 # 5、定义index函数
    23 # 6、执行index函数,实际是执行timer函数的返回值wrapper函数
    有参装饰器
  • 相关阅读:
    java实现立方和等式
    java实现立方和等式
    java实现立方和等式
    java实现立方和等式
    java实现立方和等式
    java实现拉丁方块填数字
    java实现拉丁方块填数字
    java实现拉丁方块填数字
    java实现拉丁方块填数字
    IDHTTP用法详解 good
  • 原文地址:https://www.cnblogs.com/syj888/p/10443901.html
Copyright © 2020-2023  润新知