进入python的世界_day12_python基础——函数之名称用法、闭包函数、装饰器
一、global与nonlocal(破界关键字)
1.global
如果想在局部修改全局数据
如果数据为不可变类型则需要关键字global声明
如果数据为可变类型则无需关键字global声明
a = 123
def func():
a = 321
func()
print(a)
>>>
123 # 身处何处何处开始找
————————————————————————
a = 123
def func():
global a # 破界直接修改全局范围的a
a = 321
func()
print(a)
>>>
321
2.nonlocal
如果想要在内部的局部修改外部局部的不可变类型数据
需要关键字nonlocal声明
def func():
x = 3
def func2():
nonlocal x # 把func的x修改成了1
x = 1
func2()
print(x)
func()
二、函数名的多种用法
1.可以直接当做变量名赋值
把内存地址给别的变量名,注意别加()
2.可以当作函数的实参
把某一个函数名当作参数传给另一个函数,也注意别加()
3.可以当作函数的返回值
def func():
print('来自 func')
def rua(x):
return x
res=rua(func) # run(func的内存地址,名字而已别加括号哦)
print(res) # res=func的内存地址
res() # 相当于调用了函数func()
>>>
<function func at 0x000002181B64FB80>
来自 func
4.函数名可以当做容器类型(内部可以存放多个数据)的元素
三、闭包函数
1.介绍
名称空间与作用域+函数嵌套+函数对象
核心点:名字的查找关系是以函数定义阶段为准
"闭''指该函数是内嵌函数,不能是全局层面的函数
"包"指该函数包含该函数外层函数(不能是全局层面)层面名字或者参数的引用
只要不是我的,外面第几层无所谓,相当于去外面偷
def a1():
def a2():
pass
# 现在a2没有引用a1的名字,所以不是包函数,只是闭函数
——————————————————————————————————————————————————————————
def a1():
x = 666
def a2():
x
2.闭包函数是为了干什么?
传参过程中,有些参数要重复传,为了更好的传参包一个函数来应对
传参的方式:
- 直接把函数体需要的参数定义成形参
- 用一个新的函数体把原函数体包起来,然后新函数体反回原函数体的名称,后续把新函数体赋值给一个变量名使得原函数体的内存地址又纂回手上 ,以后直接调变量名即可传参给原函数,新函数体只是个载体————只要投食一次,就可以拿这个变量名一直调用原函数体(可以这么理解,给原函数体加个爹再加个爷)
重新补充下,闭包函数的精髓是:返回的既可以是函数对象也可以是值,可以对原函数重新命名,可以为原函数提供新的参数,并提高原有参数的可拓展性,可以为原函数添加新功能而不改变调用方式
四、time模块部分功能
time.time() # 时间戳(距离1970-01-01 00:00:00所经历的秒数)
time.sleep() # 代码阻塞时长,单位默认S(秒)
时间相关操作例子:
#先进入模块
import time
def func(x,y):
start = time.time()
time.sleep(3)
print('func %s %s' % (x, y))
stop = time.time()
print(stop - start)
func(666,999)
>>>
func 666 999
3.0114011764526367
五、装饰器
1.含义
相当于一个工具,可以是函数的形式,为其他事物点缀东西(功能)但是不改变事物本身
2.为何要用装饰器
开放封闭原则:开发,有的代码是开放的,可以在源代码的基础上拓展功能
封闭,源代码封闭,不支持直接修改
所以说如果代码开放着上线后,如果功能正常你又去拓展一些功能,新增代码的过程不可能绝对保证源代码正常运行,理论上源代码都是封闭的,就可以用装饰器在不修改被装饰对象源代码以及调用方式的情况下实现新功能的添加
拿上面时间模块使用的例子来说明装饰器的好处吧
首先,我们想统计一个func(x,y)函数体运行的时间长度(因为太短,主动添加3秒阻塞)
原函数体代码:
def func(x,y):
print('func %s %s' % (x, y))
func(666,999)
————————————————————————————
方法一:直接改动了源代码
import time
def func(x,y):
start = time.time()
time.sleep(3)
print('func %s %s' % (x, y))
stop = time.time()
print(stop - start)
func(666,999)
————————————————————————————
方法二:不动用源代码,每次使用源代码给他上下统计一次时间戳
import time
def func(x,y):
time.sleep(3)
print('func %s %s' % (x, y))
start = time.time()
func(666,999)
stop = time.time()
print(stop - start)
start = time.time()
func(666,999)
stop = time.time()
print(stop - start)
# 确实源代码没动,调用方式也没动,但是每次想统计func(x,y)函数体运行的时间长度都要写一次代码,造成代码冗余,容易放假回家
————————————————————————————
方法三、原函数体不动,新造一个函数体执行这个功能,以后直接拿新函数体的名称即可实现功能
import time
def func(x,y):
time.sleep(3)
print('func %s %s' % (x, y))
def hi():
start = time.time()
func(666,999)
stop = time.time()
print(stop - start)
hi()
# 源代码没动,调用方式动了,而且传参写死了
————————————————————————————
方法三优化:原函数体不动,造一个装饰器,伪造成原函数体变量名,给装饰器传参也意味着给原函数体传参,调用方式明显没动,而且不管以后想给哪个函数体用该装饰器功能都可以适用
import time
def index(x,y):
time.sleep(3)
print('index %s %s %s' %(x,y))
def home(name):
time.sleep(2)
print('欢迎 %s 登录主页' %name)
def outter(func):
def wrapper(*args,**kwargs):
start=time.time()
res=func(*args,**kwargs)
stop=time.time()
print(stop - start)
return '执行完了'
return wrapper
# 偷梁换柱:home这个名字指向的wrapper函数的内存地址
home=outter(home)
res=home('laosong')
print('返回值--》',res)
3.装饰器的模板
def outter(func):
def wrapper(*args, **kwargs):
# 1、调用原函数
# 2、为其增加新功能
res = func(*args, **kwargs)
return res
return wrapper
六、装饰器的语法糖
装饰器准备好后(一般会有很多装饰器对应不同功能的装饰),在被装饰对象正上方的单独一行写@装饰器名字,本质就是给装饰器函数传参并赋值给被装饰对象的函数名(假扮的作用)可以理解为,准备好了锤子、锯子、起子很多工具后,想用啥工具,就@工具名
七、作业
1.编写一个用户认证装饰器
函数:register login transfer withdraw
基本要求
执行每个函数的时候必须先校验身份 eg: jason 123
拔高练习(有点难度)
执行被装饰的函数 只要有一次认证成功 那么后续的校验都通过
提示:全局变量 记录当前用户是否认证

浙公网安备 33010602011771号