名称空间 name space
有变量 x=1,1存放在内存里,存放x与1对应关系的地方叫做名称空间
locals:函数内的名称空间,包括据局部变量和形参
globals:全局变量,函数定义所在模块的名称空间
builtins:内置模块的名称空间
不同变量的作用域不同就是由这个变量所在的命名空间决定的
作用域即范围
全局范围:全局存活,全局有效
局部范围:局部存活,局部有效
查看作用域的方法:globals(),locals()
作用域查找顺序:
1 level = 'L0' 2 n = 22 3 4 def func(): 5 level = 'L1' 6 n = 33 7 print(locals(), n) 8 9 def outer():10 n = 4411 level = 'L2'12 print(locals(), n)13 14 def inner():15 level = 'L3'16 print(locals(), n)17 inner()18 outer()19 20 func()
打印结果:
{'n': 33, 'level': 'L1'} 33{'level': 'L2', 'n': 44} 44{'level': 'L3', 'n': 44} 44
LEGB 代表名称空间查找顺序:locals -> enclosing function -> globals -> __builtins__
locals:函数内的名称空间,包括据局部变量和形参
enclosing:外部嵌套函数的名称空间
globals:全局变量,函数定义所在模块的名称空间
builtins:内置模块的名称空间
闭包
函数定义和函数表达式位于另一个函数的函数体内(嵌套函数)。而且这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包。也就是说,内部函数会在外部函数返回后被执行。而当这个内部函数执行时,它仍然必须访问其外部函数的局部变量、参数以及其他内部函数。这些局部变量、参数和函数声明(最初时)的值是外部函数返回时的值,但也会受到内部函数的影响。
1 def outer(): 2 name = 'alex' 3 4 def inner(): 5 print("在inner里打印外层函数的变量", name) 6 7 return inner 8 9 10 f = outer()11 print(f) #.inner at 0x7f2613709950>12 13 f() # 在inner里打印外层函数的变量 alex
闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得该函数不论在何时调用,优先使用自己外层包裹的作用域
装饰器 decorator
其本质是一个返回函数的高阶函数
为什么要使用装饰器:软件开发的”开放-封闭“原则
封闭:已实现的功能代码块不应该被修改
开放:对现有功能的扩展开放
装饰器的原则:
1、不修改被装饰对象的源代码
2、不修改被装饰对象的调用方式
装饰器的目标:在遵循1和2的前提下,为被装饰对象添加上新功能
1 # 无参数装饰器 2 import time 3 4 5 def timer(func): 6 def inner(): 7 start = time.time() 8 func() 9 print(time.time()-start)10 return11 return inner12 13 14 @timer15 def func1():16 time.sleep(1)17 print('111')18 19 20 func1()
1 # 有参数装饰器 2 def login(func): 3 def inner(*args, **kwargs): 4 username = input('user:') 5 password = input('passwd:') 6 if username == 'alex' and password == '123': 7 print('welcome') 8 return func(*args, **kwargs) 9 else:10 print('wrong')11 return12 return inner13 14 15 @login16 def func2(n):17 print(n*n)18 19 func2(5)
列表生成式
现有列表[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],把列表中每一个元素+1
1 # 使用循环 2 a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 3 4 b = [] 5 for i in a: 6 b.append(i+1) 7 a = b 8 print(a) 9 10 11 a = map(lambda x: x+1, a)12 print(a)13 for i in a:14 print(i)
更简单的方法 是使用列表生成式:
1 a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]2 3 a = [i+1 for i in a]4 print(a)
生成器
通过列表生成式,我们可以直接创建一个列表。但是,受内存限制,列表的容量是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面的几个元素,那后面绝大多数元素占用的空间就白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那么我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的列表,从而节省大量的空间。在python中,这种一边循环一边计算的机制,称为生成器:generator
要创建一个生成器,只要把列表生成式的 [] 改成(),就创建了一个生成器:
1 L = [i*i for i in range(7)]2 print(L)3 4 g = (i*i for i in range(7))5 print(g)
L是一个列表,g 是一个生成器:
[0, 1, 4, 9, 16, 25, 36]at 0x7f1aa828f468>
列表的每一个元素可以直接打印出来,生成器的每一个元素可以通过next() 函数调用 或者 for 循环打印
1 print(next(g)) 2 # 使用next打印8次,结果如下: 3 4 0 5 1 6 4 7 9 8 16 9 2510 3611 Traceback (most recent call last):12 File "/mnt/模块02/生成器.py", line 39, in13 print(next(g))14 StopIteration
generator保存的是算法,每次调用next(g) ,就计算出g的下一个元素的值,直到计算出最后一个元素,没有下一个元素时,抛出StopIteration的错误
使用for循环则不会报错
1 for i in g: 2 print(i) 3 4 # 结果 5 0 6 1 7 4 8 9 9 1610 2511 36
如果算法比较复杂,用列表生成式无法实现时,可以用函数来实现
比如:斐波拉契数列
1 def fib(x): 2 n, a, b = 0, 0, 1 3 while n < x: 4 print(b) 5 a, b = b, a+b 6 n += 1 7 return 'done' 8 9 fib(10)10 11 112 113 214 315 516 817 1318 2119 3420 55
fib函数实际上定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑已经非常类似生成器
只要把print(b) 改成yield b 就形成了一个生成器
1 def fib(x): 2 n, a, b = 0, 0, 1 3 while n < x: 4 yield b 5 a, b = b, a+b 6 n += 1 7 return 'done' 8 9 b = fib(5)10 print(b) #
如果一个函数定义中包含yield 关键字,那么这个函数就是一个generator
generator的执行流程:
每次调用next() 的时候执行,遇到yield 语句返回,再次被next() 调用时从上次返回的yield 语句处继续执行
1 def range2(n): 2 3 count = 0 4 while count < n: 5 print('count', count) 6 count += 1 7 sign = yield count 8 print('---sign', sign) 9 10 if sign == 'stop':11 break12 print('----hanshu')
a = range2(3)print(a) #r1 = next(a) # count 0print(r1) # 1print('干点别的') # 干点别的r2 = next(a) # ---sign None # count 1print(r2) # 2a.send('stop') # ---sign stop # ----hanshu # Traceback (most recent call last): # File "/mnt/模块02/生成器.py", line 121, in # a.send('stop') # StopIteration
a = range2(3)print(a) #r1 = next(a) # count 0print(r1) # 1print('干点别的') # 干点别的r2 = next(a) # ---sign None # count 1print(r2) # 2print('tinghui') # tinghuir3 = a.__next__() # ---sign None # count 2print(r3) # 3r4 = a.__next__() # ---sign None # ----hanshu # Traceback (most recent call last): # File "/mnt/模块02/生成器.py", line 136, in # r4 = a.__next__() # ---sign None # StopIteration
迭代器
可迭代对象 Iterable :可以直接作用于for循环的对象
使用 isinstance() 判断一个对象是否是Iterable对象
>>> from collections import Iterable>>> isinstance([],Iterable)True>>> isinstance({},Iterable)True>>> isinstance('abc',Iterable)True>>> isinstance((x*x for x in range(10)), Iterable)True>>> isinstance(100, Iterable)False>>> isinstance((1,2,3), Iterable)True>>>
迭代器 Iterator :可以被next() 函数调用并不断返回下一个值的对象
同样使用isinstance() 判断
>>> from collections import Iterator>>> isinstance([],Iterator)False>>> isinstance({},Iterator)False>>> isinstance((x*x for x in range(10)), Iterator)True>>> isinstance('abc',Iterator)False>>>
生成器都是Iterator 对象,但list, dict, str 虽然是Iterable,却不是Iterator
把list, dict, str 等Iterable 变成 Iterator 可以使用iter() 函数:
>>> isinstance(iter([]), Iterator)True>>> isinstance(iter('abc'), Iterator)True>>>
小结:
1、凡是可作用于for循环的对象都是Iterable 类型
2、凡是可作用于next() 函数的对象都是Iterator 类型,他们表示一个惰性计算的序列
3、集合数据类型如list, dict, str 等是Iterable 但不是Iterator, 不过可以通过iter() 函数获得一个Iterator 对象