命名空间
名称空间是存放变量名和赋值绑定关系的地方
名称空间共 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() 函数实现的