美文网首页我爱编程
Python之路11:函数之命名空间,闭包,生成器,迭代器

Python之路11:函数之命名空间,闭包,生成器,迭代器

作者: 缘小泽 | 来源:发表于2018-04-13 11:42 被阅读0次

    命名空间

    名称空间是存放变量名和赋值绑定关系的地方

    名称空间共 3 种:

    • locals: 是函数内的名称空间,包括局部变量和形参
    • globals: 全局变量,函数定义所在模块的名称空间
    • builtins: 内置模块的名称空间

    不同变量的作用域不同是由这个变量所在的命名空间决定的
    作用域范围:

    • 全局变量:全局有效,全局存活
    • 局部变量:局部有效,局部存活

    查看作用域的方法:globals(), locals()
    作用域的查找规则:LEGB

    闭包

    闭包:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层的作用域

    def func():
        name = 'jack'
    
      def inner():
            print('函数变量:', name)
    
        return inner
    
    f = func()
    
    f()
    # 返回结果: 函数变量:jack
    

    生成器

    列表生成式

    # 将列表中的每一个值都进行自乘
    # 可以通过列表生成式来完成
    >>> a = list(range(10))
    >>> a
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    >>> a = [i*i for i in a]
    >>> a
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    
    # 以上就是通过简单的列表生成式来实现的
    

    生成器:
    通过列表生成式,可以直接创建一个列表,但是受到内存限制,列表的容量是有限的。
    列表元素通过某种算法,再循环中不断推算出后续的元素,这种一边循环一边计算的机制,称为生成器:generator

    # 要创建一个 generator 很简单,就是将列表生成式中的 [] 改成 ()
    >>> l = [i*i for i in range(10)]
    >>> l
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    >>> g = (x*x for x in range(10))
    >>> g
    <generator object <genexpr> at 0x7f77ffd6cdb0>
    
    # 创建 l 和 g 的区别就是最外层的[] 和 (), l 是一个list,而 g 是 generator,可以直接打印获取 list 所有元素,但 generator 需要通过 next() 一个一个获取
    >>> next(g)
    0
    >>> next(g)
    1
    >>> next(g)
    4
    >>> next(g)
    9
    >>> next(g)
    16
    >>> next(g)
    25
    >>> next(g)
    36
    >>> next(g)
    49
    >>> next(g)
    64
    >>> next(g)
    81
    >>> next(g)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    StopIteration
    # 直到最后获取不到抛出 StopIteration 错误
    # 但用上面 next(g) 获取太麻烦,可以通过 for 循环
    >>> g = (x*x for x in range(10))
    >>> for n in g:
    ...   print(n)
    ... 
    0
    1
    4
    9
    16
    25
    36
    49
    64
    81
    # 而且这样也不会报错
    

    著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数可由前两个数相加所得
    斐波拉契数列通过列表生成式无法写出来,但是可以通过函数打印出来

    def fin(max):
        n, a, b = 0, 0, 1
        
        while n < max:
            print b
            a, b = b, a+b
            
            n += 1
        return 'done'
    
    data = fin(5)
    print(data)
    # 执行结果为
    1
    1
    2
    3
    5
    
    # 上面 fin 函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素推算出后续任意的元素
    # 要讲上面函数变成 generator,只需要把 print b 改为 yield b
    def fin(max):
        n, a, b = 0, 0, 1
        
        while n < max:
            # print b
            yield b
            a, b = b, a+b
            
            n += 1
        return 'done'
    
    data = fin(5)
    print(data)
    # 执行结果:<generator object fib at 0x00000232F05DEF68>
    
    # 这是函数中定义 generator 的一种方法,如果一个函数内包含 yield 字段,那么这个函数就不再是一个普通函数,而是一个 generator
    
    # generator执行过程和函数是不同的,函数执行是顺序执行,需要 return语句或者最后一行语句返回,而 generator函数,则是每次调用 next() 的时候执行,遇到 yield语句返回,如没有next(),函数执行到 yield 是就停止不再继续执行了,再次被 next() 调用时从上次返回的 yield 语句继续执行下,在次遇到 yield 语句返回并停止
    

    迭代器

    • 可迭代对象
      可以直接作用于 for 循环的对象称为可迭代对象:Iterable
      可直接作用于 for 循环的数据类型有以下几类:
      一类是集合数据类型:list ,tuple, dict, set, str 等
      一类是 generator,包括生成器和带 yield 的generator functions
      可以使用 isinstance() 判断一个对象是否是 Iterable(可迭代)对象
    >>> from collections import Iterable
    >>> isinstance([], Iterable)
    True
    >>> isinstance({}, Iterable)
    True
    >>> isinstance((), Iterable)
    True
    >>> isinstance('abc', Iterable)
    True
    >>> isinstance(123, Iterable)  # 整数就不是可迭代对象
    False
    

    而生成器不但可以作用于 for 循环,还可以被 next() 函数不断的调用并返回下一个值,直到最后抛出 Stoplteration 错误并返回

    • 迭代器
      可以被 next() 函数调用并不断返回下一个值的对象称为 迭代器:Iterator
      可以用 isinstance()判断一个对象是否是 Iterator(迭代)对象:
    >>> isinstance((i for i in range(10)), Iterator)
    True
    >>> isinstance([], Iterator)
    False
    >>> isinstance('abc', Iterator)
    False
    
    # 生成器都是 Iterator(迭代器)对象,但 list,str, dict 虽然是 Iterable(可迭代对象), 但不是 Iterator(迭代器)
    

    可以通过 iter() 函数把 Iterable 变成 Iterator

    >>> isinstance(iter([]), Iterator)
    True
    >>> isinstance(iter('abc'), Iterator)
    True
    

    Python 中 Iterator 对象表示的是一个数据流,Iterator 对象可以被 next() 函数调用并不断返回下一个数据,直到没有数据抛出 StopIteration 错误,可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

    • 总结

    凡是可作用于 for 循环的对象都是 Iterable 类型
    凡是可作用于 next() 函数的对象都是 Iterator 类型,它们表示一个惰性计算的序列
    集合类型如 list,dict,str等都是 Iterable,但不是 Iterator, 但是可以通过 iter() 函数获得一个 Iterator 对象
    python3 的 for 循环本质上就是通过不断调用 next() 函数实现的

    相关文章

      网友评论

        本文标题:Python之路11:函数之命名空间,闭包,生成器,迭代器

        本文链接:https://www.haomeiwen.com/subject/lelckftx.html