函数进阶
名称空间
又称name space,顾名思义就是存放名字的地方,存放什么名字呢?举例说明,若变量x=1,1存放于内存,那名字x存放在哪里呢?名称空间正式存放名字x与1绑定关系的地方
名称空间共有3种,分别如下:
- locals:是函数内的名称空间,包括局部变量和形参
- globals:全局变量,函数定义所在模块的名字空间
- builtins:内置模块的那字空间
不同变量的作用域不同正是由这个变量所在的命名空间决定的。
作用域即范围:
- 全局范围:全局存活,全局有效
- 局部范围:临时存活,局部有效
查看作用域的方法 globals(),locals()
作用域查找顺序
level = 'L0'
n = 22
def func():
level = 'L1'
n = 33
print(locals())
def outer():
n = 44
level = 'L2'
print(locals(),n)
def inner():
level = 'L3'
print(locals(), n) # 此处打印的n是多少
inner()
outer()
func()
结果
{'level': 'L1', 'n': 33}
({'n': 44, 'level': 'L2'}, 44)
({'n': 44, 'level': 'L3'}, 44)
加载顺序:内置名称空间------>全局名称空间----->局部名称空间
名字的查找顺序:局部名称空间------>全局名称空间----->内置名称空间
inner()里打印的n值是多少?
inner()是嵌套在outer()里面的,inner()中未定义n,但是outer()中有定义n=44,对于inner()来说n是outer()中的全局变量,也就是说n的作用域是整个outer()。
闭包
在一些语言中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。闭包可以用来在一个函数与一组“私有”变量之间创建关联关系。在给定函数被多次调用的过程中,这些私有变量能够保持其持久性。—— 维基百科
详细参考:
闭包函数的必要条件:
- 闭包函数必须返回一个函数对象
- 闭包函数返回的那个函数必须引用外部变量(一般不能是全局变量),而返回的那个函数内部不一定要return
def outer():
a = 1 #外部函数
def inner():
print(a) #内部函数
inner()
print(inner.__closure__)
outer()
print(outer.__closure__)
结果
1
(<cell at 0x00000205587BE918: int object at 0x00007FFF717CE350>,) # 证明inner()是外包
None # 证明outer()不是闭包
闭包的作用就是减少函数调用时外部函数相同代码的执行次数,比如每次调用outer()函数时,都会创建一次a=1,如果利用下面的方法可以直接调用内部函数,不用每次都创建a=1。
def outer():
a = 1
def inner():
print(a)
return inner # 返回inner()函数的地址
inn = outer() # inn接收inner()函数地址,并作为对象
inn() # 调用对象
print(inn.__closure__)
结果
1
(<cell at 0x00000213C056E918: int object at 0x00007FFF5E5AE350>,)
闭包——初试爬虫
# import urllib
from urllib.request import urlopen
def func():
url = "http://www.xiaohuar.com/"
def inner():
ret = urlopen(url).read()
print(ret)
return inner
get_func = func()
get_func()
装饰器
装饰器形成的过程
用一个例子来说明。公司绩效考核时老板要看每个员工所写代码的执行时间,要求写一个函数来计算时间
最先想到的是这样的写法:
import time # 导入time模块
def timer():
start = time.time() # 记录开始时间
time.sleep(0.1) # 用来延时以便显示时间
print("大家好!")
end = time.time() # 记录结束时间
return end-start
t = timer()
print(t)
虽然能够成功计算出执行时间,不过问题来了,假设有很多个函数,
总不能每个函数体中都加上start------end这个部分吧。就又想了一招:
import time
def func():
time.sleep(0.1)
print("大家好!")
def timer(f):
start = time.time()
f()
end = time.time()
return end - start
t = timer(func)
print(t)
成功地解决了穿“统一服装”的问题,这时人家又会想,timer()是个神马玩意,我只想看func()的执行时间,不想用timer(),那怎么办?
那我们可以让调用func的时候实际调用的是timer,是不是和闭包有点联系呢!
import time
def func():
time.sleep(0.1)
print("大家好!")
def timer(f):
def inner():
start = time.time()
f()
end = time.time()
print(end - start)
return inner
func = timer(func)
func()
装饰器的作用
- 不想修改函数的调用方式,但是还是想在原来的函数前后添加功能
- timer()就是一个装饰器函数,只是对一个函数有一些装饰作用
原则:开放封闭原则
- 开放:对扩展是开放的
- 封闭:对修改是封闭的
装饰器的固定模式
首先介绍一个习惯用语——语法糖,即“@装饰器函数名”,要紧挨着被装饰函数
import time
def timer(f):
def inner():
start = time.time()
f()
end = time.time()
print(end - start)
return inner
@timer # 相当于func = timer(func),紧挨着被装饰函数func()
def func():
time.sleep(0.1)
print("大家好!")
# func = timer(func)
func()
做一些修改,再深入理解一下
import time
def timer(f):
def inner():
start = time.time()
f()
end = time.time()
print(end - start)
return inner
@timer # 相当于func = timer(func),紧挨着被装饰函数func()
def func():
time.sleep(0.1)
print("大家好!")
return "123"
# func = timer(func)
ret = func()
print(ret)
结果
大家好!
0.10089230537414551
None
为什么没有返回值123呢?因为装饰器@timer,相当于执行func = timer(func),此时的func相当于inner,inner是没有返回值的,所以ret收到的是None。再改一下:
import time
def timer(f):
def inner():
start = time.time()
ret = f()
end = time.time()
print(end - start)
return ret
return inner
@timer # 相当于func = timer(func),紧挨着被装饰函数func()
def func():
time.sleep(0.1)
print("大家好!")
return "123"
# func = timer(func)
ret = func()
print(ret)
结果
大家好!
0.10078144073486328
123
此时就有了123,因为在inner()中有了返回值ret
接下来学习一下装饰带参数函数的装饰器,注意a的位置
import time
def timer(f):
def inner(a):
start = time.time()
ret = f(a)
end = time.time()
print(end - start)
return ret
return inner
@timer # 相当于func = timer(func),紧挨着被装饰函数func()
def func(a):
time.sleep(0.1)
print("大家好!", a)
return "123"
# func = timer(func)
ret = func(1)
print(ret)
结果
大家好! 1
0.10086464881896973
123
还没完,看看多个参数怎么办?
import time
def timer(f):
def inner(*args): # 这里就用到了不固定传参方式
start = time.time()
ret = f(*args) # 这里就用到了不固定传参方式
end = time.time()
print(end - start)
return ret
return inner
@timer # 相当于func = timer(func),紧挨着被装饰函数func()
def func1(a):
time.sleep(0.1)
print("大家好!", a)
return "123"
@timer # 相当于func = timer(func),紧挨着被装饰函数func()
def func2(a, b):
time.sleep(0.1)
print("大家好!", a, b)
return "123"
# func = timer(func)
ret1 = func1(1)
ret2 = func2(1,2)
print(ret1, ret2)
结果
大家好! 1
0.10072922706604004
大家好! 1 2
0.10012173652648926
123 123
固定模式
def wrapper(func): # wrapper装饰器,func=qqxing
def inner(*args,**kwargs):
ret = func(*args,**kwargs)
return ret
return inner
@wrapper # 相当于qqxing=wrapper(qqxing)
def qqxing():
print('123')
ret = qqxing()