小白笔记
仅记录常规操作中较为不熟悉的操作类型
1、作用域
作用域是程序运行时变量可被访问的范围,定义在函数内的变量是局部变量,局部变量的作用范围只能是函数内部范围内,它不能在函数外引用。
定义在模块最外层的变量是全局变量,它是全局范围内可见的,当然在函数里面也可以读取到全局变量的。例如:
num=10 # 全局作用域变量
def foo():
print(num) # 10
而在函数外部则不可以访问局部变量。例如:
def foo():
num=10
print(num) # NameError: name 'num' is not defined
2、嵌套函数
函数不仅可以定义在模块的最外层,还可以定义在另外一个函数的内部,像这种定义在函数里面的函数称之为嵌套函数(nested function)例如:
def print_msg():
# print_msg 是外围函数
msg="zen of python"
def printer():
# printer是嵌套函数
print(msg)
printer()
# 输出 zen of python
print_msg()
对于嵌套函数,它可以访问到其外层作用域中声明的非局部(non-local)变量,比如代码示例中的变量 msg 可以被嵌套函数 printer 正常访问。
那么有没有一种可能即使脱离了函数本身的作用范围,局部变量还可以被访问得到呢?答案是闭包
3、什么是闭包
函数身为第一类对象,它可以作为函数的返回值返回,现在我们来考虑如下的例子:
def print_msg():
# print_msg 是外围函数
msg="zen of python"
def printer():
# printer 是嵌套函数
print(msg)
return printer
another=print_msg()
# 输出 zen of python
another()
这段代码和前面例子的效果完全一样,同样输出 "zen of python"。不同的地方在于内部函数 printer 直接作为返回值返回了。
一般情况下,函数中的局部变量仅在函数的执行期间可用,一旦 print_msg() 执行过后,我们会认为 msg变量将不再可用。然而,在这里我们发现 print_msg 执行完之后,在调用 another 的时候 msg 变量的值正常输出了,这就是闭包的作用。闭包使得局部变量在函数外被访问成为可能。
这里的 another 就是一个闭包,闭包本质上是一个函数,它有两部分组成,printer 函数和变量 msg。闭包使得这些变量的值始终保存在内存中。
函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数,如上例中的 another()。
闭包,顾名思义,就是一个封闭的包裹,里面包裹着自由变量,就像在类里面定义的属性值一样,自由变量的可见范围随同包裹,哪里可以访问到这个包裹,哪里就可以访问到这个自由变量。
4、带参数的闭包
def adder(x):
def wrapper(y):
return x + y
return wrapper
adder5 = adder(5) # 执行完毕后,变量adder5指向了函数wrapper
# 输出 15
adder5(10) # 执行是调用函数wrapper,传入参数 10,输出: 5(adder(5)中参数)+10
# 输出 11
adder5(6) # 5 + 6
闭包,可以理解为嵌套函数的特殊形式,函数返回值为子函数,且子函数调用的上级函数变量,不被释放,可在函数外被多次调用.
返回闭包时牢记一点:返回函数不要引用任何循环变量,否则后续会发生变化的变量
def count():
fs= []
for i in range(1,4):
def f():
return i*i
fs.append(f)
'''
返回函数引用变量i,但它并非立刻执行。
等到3个函数都返回时,它们所引用的变量i已经变成了3
因此最终结果全部为9
'''
return fs
f1, f2, f3= count()
print(f1(),f2(),f3())
如果一定要引用循环变量怎么办?方法是再创建一个函数 g(j) ,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:
def count():
def g(j):
def f():
return j*j
return f
fs= []
for i in range(1,4):
fs.append(g(i)) # f(i)立刻被执行,因此i的当前值被传入f()
return fs
f1, f2, f3= count()
print(f1(),f2(),f3())
5、装饰器的概念
对函数(或方法)进行装饰,可以在函数运行前,增强函数的功能
严格来讲,装饰器就是一个闭包函数,它的参数是:要增强功能的函数
import time
def timer(func):
def inner():
start_time= time.time()
func() # 被增强的函数在这里执行
end_time= time.time()
print('程序运行时间:', end_time- start_time)
return inner
@timer
def test():
time.sleep(3)
print('test!')
if __name__== '__main__':
test()
对函数使用装饰器后,主函数中 test() 实际执行的是:timer(test)
6、装饰器的运行顺序
装饰器在被装饰的函数定义之后立即运行,通常是在导入时(即python加载模块时)
def outer(func):
print("%s outer running" % func)
def inner():
print("%s inner running" % func)
return func() # 如果return func 则不会运行func函数
return inner
@outer
def test1():
print("test1 runing")
@outer
def test2():
print("test2 runing")
def test3():
print("test3 runing")
def main():
print("main runing")
test1()
test2()
test3()
if __name__ == "__main__":
main()
运行结果如下:
# 装饰器运行结果
<function test1 at 0x1040cc730> outer running # 返回了函数 inner()
<function test2 at 0x1040cc840> outer running
# 主函数运行结果
main runing
<function test1 at 0x1040cc730> inner running # 运行test1() 时,继续执行装饰器返回的 inner() 函数
test1 runing
<function test2 at 0x1040cc840> inner running
test2 runing
test3 runing
可以看出,在函数test1、test2定义后,装饰器outer立即被执行。
7、带参数的装饰器
如果函数要增强的功能,需要按不同的定位(参数),输出不同的增强信息,就需要建立一个带参数的装饰器
要实现带参数的装饰器,就要用两层的嵌套函数来实现
def say_hello(country):
def wrapper(func):
def deco(*args,**kwargs): # 最终的返回函数
if country== "china":
print("你好!")
elif country== "america":
print('hello.')
else:
return
# 真正执行函数的地方
func(*args,**kwargs)
return deco
return wrapper
@say_hello("china")
def chinese():
print("我来自中国。")
@say_hello("america")
def american():
print("I am from America.")
if __name__== '__main__':
chinese()
american()
运行结果:
你好!
我来自中国。
hello.
I am from America.
注:修饰器修饰过的函数,会把被修饰函数的‘ __ name __ ’变为返回函数‘inner’ 或 ‘deco’ ,为防止有些依赖函数签名的代码执行不出错,需要将原始函数的属性复制到返回函数中。具体实现在定义的修饰器的返回函数前,增加一个 @functools.wraps(func) 内部修饰器,如下:
def outer(func):
print("%s outer running" % func)
@functools.wraps(func) # 函数前需要导入 import functools 库
def inner():
print("%s inner running" % func)
return func() # 如果return func 则不会运行func函数
return inner
同理,带参数的装饰器进行同样的处理即可
参考链接:
https://zhuanlan.zhihu.com/p/26934085
https://zhuanlan.zhihu.com/p/68810314
https://zhuanlan.zhihu.com/p/65968462
https://www.liaoxuefeng.com/wiki/1016959663602400/1017451662295584