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

命名空间

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

名称空间共 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
 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 "", line 1, in 
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 的一种方法,如果一个函数内包含 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:函数之命名空间,闭包,生成器,迭代器)