一. 函数闭包
【locals()打印当前环境的局部变量】
闭包:在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,则把这个使用外部函数变量的内部函数称之为闭包
# 1.定义内部函数:
def outer(num):
# 2.定义内部函数,在内部函数中使用外部函数的变量
def inner(num1):
print(num + num1)
# 3.外部函数返回内部函数的地址
return inner # 函数名字就是函数地址
if __name__ == '__main__':
# 创建闭包实例
func = outer(100) # 此时func相当于是inner
# 调用闭包
func(10) # 此时调用inner函数,保存的num值为100
# 可以创建多个闭包实例,不同的闭包实例之间,不会相互影响
# 输出
110
函数内部想要修改全局变量,使用global关键字。在闭包函数内部,想要修改外部函数的局部变量,则使用nonlocal关键字
def outer():
num = 10
def inner():
# num = 100 # 不是修改外部变量的值,重新定义的局部变量
nonlocal num # 声明使用外部变量 num 不重新定义
num = 100
print(f'调用inner之前:{num}')
inner()
print(f'调用inner之后:{num}')
return inner
func = outer()
func()
# 输出
调用inner之前:10
调用inner之后:100
二. 装饰器
本质是函数,功能是为其他函数添加附加功能
原则:
- 不修改被修饰函数的源代码
- 不修改被修饰函数调用方法
装饰器 = 高阶函数 + 函数嵌套 + 闭包
高阶函数:
- 函数接收的参数是一个函数名
- 函数的返回值是一个函数名
- 满足上述条件任意一个,都可称为高阶函数
使用高阶函数实现装饰器
import time
def foo():
print('test one!')
def timer(func):
start_time = time.time()
func()
stop_time = time.time()
print('Running Time is %s' %(stop_time - start_time))
return func
f00 = timer(foo)
f00()
# 输出
test one!
Running Time is 0.0
test one!
# 返回了func,多运行了一次
import time
def foo():
print('test one!')
def timer(func):
start_time = time.time()
return func
stop_time = time.time()
print('Running Time is %s' %(stop_time - start_time))
foo = timer(foo)
foo()
# 输出
test one!
# 又没有实现计时功能,在中间就已经结束了
高阶函数虽然没有修改被修饰函数的源代码,也没有修改被修饰函数的调用方式,但是也没有为被修饰函数添加新功能,所以高阶函数不足够实现装饰器
装饰器框架
def timmer(func):
def wrapper():
print(func)
func()
return wrapper
通过装饰器实现程序运行时间的计时功能
import time
def timmer(func):
def wrapper():
start_time = time.time()
func()
stop_time = time.time()
print('Running Time is %s' %(stop_time - start_time))
return wrapper
# 测试函数
@timmer # @等同于 test = timmer(test)
def test():
all = 0
for i in range(100):
time.sleep(0.0001)
all += i
return all # 返回值是空??
# test = timmer(test)
print(test()) # 实际运行的是wrapper()
# 输出
Running Time is 1.5582449436187744
None
这样虽然得到了运行时间,但却多返回了一个None值,因为在装饰器内部函数虽然执行了test()函数,但返回的是wrapper()函数,而wrapper()函数并没有返回任何值,所以输出了None
可以接收到返回值版本
#装饰器
import time
def timmer(func):
def wrapper(a, b, c):
start_time = time.time()
res = func(a, b, c)
stop_time = time.time()
print('Running Time is %s' %(stop_time - start_time))
# 在这里把func()返回值赋值后再返回
return res
return wrapper
#测试函数
@timmer #equal test = timmer(test)
def person(name, age, gender):
return '返回 %s %s %s' %(name, age, gender)
#test = timmer(test)
print(person('luke', 18, 'male'))
# 输出
Running Time is 0.0
返回 luke 18 male
如果面对不同函数,由于不同函数参数个数不同,这样的写法显然不能满足所有函数
import time
def timmer(func):
def wrapper(*args, **kwargs): # *args相当于解压序列,所以当传入超出参数个数的时候会报错
start_time = time.time()
res = func(*args, **kwargs)
stop_time = time.time()
print('Running Time is %s' %(stop_time - start_time))
return res
return wrapper
#测试函数
@timmer # test = timmer(test)
def father(name, age, gender):
return '返回信息 %s %s %s' %(name, age, gender)
def son(name, age, gender, school):
return '返回信息 %s %s %s %s' %(name, age, gender, school)
# test = timmer(test)
print(father('luke', 38, 'male'))
print(son('alex', 13, 'male', 'beijing'))
1、*args和**kwargs 这两个是python中方法的可变参数。
2、*args表示任何多个无名参数,它是一个tuple;
3、**kwargs表示关键字参数,它是一个dict。并且同时使用*args和**kwargs时,必须*args参数列要在**kwargs前,像foo(a=1, b='2', c=3, a', 1, None, )这样调用的话,会提示语法错误“SyntaxError: non-keyword arg after keyword arg”。
4、当方法的参数不确定时,可以使用*args 和**kwargs
叠加多个装饰器时
- 加载顺序(outter函数的调用顺序):自下而上
- 执行顺序(wrapper函数的执行顺序):自上而下
def outter1(func1): #func1=wrapper2的内存地址
print('加载了outter1')
def wrapper1(*args,**kwargs):
print('执行了wrapper1')
res1=func1(*args,**kwargs)
return res1
return wrapper1
def outter2(func2): #func2=wrapper3的内存地址
print('加载了outter2')
def wrapper2(*args,**kwargs):
print('执行了wrapper2')
res2=func2(*args,**kwargs)
return res2
return wrapper2
def outter3(func3): # func3=最原始的那个index的内存地址
print('加载了outter3')
def wrapper3(*args,**kwargs):
print('执行了wrapper3')
res3=func3(*args,**kwargs)
return res3
return wrapper3
@outter1 # outter1(wrapper2的内存地址)======>index=wrapper1的内存地址
@outter2 # outter2(wrapper3的内存地址)======>wrapper2的内存地址
@outter3 # outter3(最原始的那个index的内存地址)===>wrapper3的内存地址
def index():
print('from index')
print('======================================================')
index()
# 输出
加载了outter3
加载了outter2
加载了outter1
======================================================
执行了wrapper1
执行了wrapper2
执行了wrapper3
from index
解压序列
G = [1,3,4,5,6,6,6,3,2,3,23,2,12,31,231,23,12,31,23,1,244]
a, *_, c = G # a=1, c=244
python可以快速交换变量值:f1, f2 = f2, f1
而c语言则需要:temp=b; b=a; a=temp;
三. 用装饰器做一个验证功能
# 带参数,带返回值
def verify(func):
def wrapper(*args, **kwargs):
name = input('Username:')
pwd = input('Password:')
if name == 'luax' and pwd == '123':
res = func(*args, **kwargs)
flag = True
return res
else:
print('Wrong Username or Password!')
return wrapper
@verify
def index(name):
print('Welcome!%s' %name)
@verify
def home(name):
print('Welcome!%s' % name)
@verify
def cart():
print('【%s, %s, %s】' %('吃', '东', '西'))
index('luax')
home('luax')
cart()
# 每一次都要重新输入账号密码
# 无参数版本
# 用户列表
user_list = [{'name':'a', 'password':'a'},
{'name':'b', 'password':'b'},
{'name':'c', 'password':'c'},
]
# 保存当前登录用户状态
current_user = {'name':None, 'login_status':False}
def verify(func):
def wrapper(*args, **kwargs):
if current_user['name'] or current_user['login_status']:
res = func(*args, **kwargs)
return res
name = input('Username:')
pwd = input('Password:')
for user_dict in user_list:
if name == user_dict['name'] and pwd == user_dict['password']:
res = func(*args, **kwargs)
current_user['name'] = name
current_user['login_status'] = True
return res
else: #和for一个代码块,可以让if一直循环
print('Wrong Username or Password!')
return wrapper
@verify
def index(name):
print('Welcome!%s' %name)
@verify
def home(name):
print('Welcome!%s' % name)
@verify
def cart():
print('【%s, %s, %s】' %('吃', '东', '西'))
index('luax')
home('luax')
cart()
可以用全局变量和函数闭包来模拟session