一、内部函数
内部函数特点:
1、可以访问外部函数的变量
2、内部函数可以修改外部函数的可变类型变量
3、内部函数修改全局的不可变的变量时,需要在内部函数声明:global 变量名
内部函数修改外部函数的不可变的变量时,需要在内部函数中声明:nonlocal 变量名
4、locals() 查看本地变量有哪些,以字典的形式输出
globals() 查看全局变量有哪些,以字典的形式输出(注意里面会有一些系统的键值对)
def func():
#声明局部变量
n = 100
list1 = [1, 2, 3]
#声明内部函数
def inner_func():
nonlocal n #需要添加 nonlocal 取到外部函数的局部变量
for index, value in enumerate(list1):
list1[index] = value + 5
list1.sort()
# n += 100 报错,内部函数用外部需要声明 nonlocal
n += 101
inner_func() #函数定义后需要调用,调用内部函数
print(list1)
print(n)
func()
a = 100 #全局变量
#查看全局变量有哪些
print(globals())
def func1():
b = 99
def inner_func():
global a
nonlocal b
c = 88
c += 12
b += 1
a += 10
print(a, b, c)
inner_func()
#使用locals()内置函数进行查看,可以看到在当前函数中声明的内容有什么
#locals() 是一个字典 key:value
print(locals())
func1()
二、闭包
闭包 是 在函数中提出的概念
条件:
1、外部函数中定义了内部函数
2、外部函数是有返回值
3、返回的值是:内部函数名
4、内部函数引用了外部函数的变量
格式:
def 外部函数():
...
def 内部函数():
...
return 内部函数名
def func2():
value_a = 100
def inner_func():
value_b = 99
print(value_a, value_b)
return inner_func
i_func = func2() #i_func 为inner_func
#调用返回出来的内部函数
i_func()
def func3(a, b):
c = 10
def inner_func3():
s = a + b + c
print("相加之后的结果是:", s)
return inner_func3
i_func3 = func3(10, 20)
i_func3()
闭包的应用:
保存返回闭包时的状态(外层函数变量)
def func4(a, b):
c = 10
def inner_func():
s = a + b + c
return inner_func
#两个内部函数地址不同,均为新建
i_func4_1 = func4(2, 8)
print(i_func4_1) 输出:i_func4_1 内存地址
i_func4_2 = func4(5, 10)
print(i_func4_2) 输出:i_func4_2 内存地址 两个地址不相同
#计数器
def generate_count():
container = [0]
def add_one():
container[0] = container[0] + 1
print("当前是第{}次访问".format(container[0]))
return add_one
#内部函数就是一个计数器
counter = generate_count()
counter() #第一次的访问 输出:...第一次...
counter() 输出:...第二次...
counter() 输出:...第三次...
闭包的缺点:
缺点1:作用域没有那么直观
缺点2:因为变量不会被垃圾回收,所以有一定的内存占用问题
闭包的作用:
1、可以使用同级的作用域
2、读取其它元素的内部变量
3、延长作用域
闭包的总结:
1、闭包似优化了变量,原来需要类对象完成的工作,闭包也可以完成
2、由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存
3、闭包的好处,使代码变得简洁,便于阅读代码
4、闭包是理解装饰器的基础
def func5():
a = 100
def inner_func():
b = 90
s = a + b
print(s)
def inner_func2():
# print(b) 不能输出b
inner_func()
return "hello"
return inner_func2 #返回定义的函数地址
i_func5 = func5()
str_i_func2 = i_func5()
print(str_i_func2)
三、装饰器
特点:(将函数作为参数传递)
1、函数A是作为参数出现的(函数B就接收函数A作为参数)
2、要有闭包的特点
def func6(number):
a = 100
def inner_func():
nonlocal a
nonlocal number
number += 1
for i in range(number):
a += 1
print("修改后的a值:", a)
return inner_func
i_func6 = func6(5)
i_func6()
#函数作为参数
a = 50
i_func6_2 = func6(a) #a是一个实参
print(i_func6_2)
a = 10 #声明整型变量
b = a
#地址引用
def test():
print("------test------")
t = test #声明函数 t也指向test函数的地址
def func7(f):
print(f) #打印test地址
f() #调用函数
print("------->func7")
func7(test)
定义一个装饰器:
def decorate(func):
a = 100
def wrapper(): #内层 包装
func()
print("------>刷漆、铺地板")
print("------装修完成")
return wrapper
使用装饰器 @装饰器函数的函数名
1、house 被修饰函数
2、将被装饰函数作为参数传给装饰器decorate
3、执行decorate函数
4、将返回值又赋值给house
@decorate
def horse():
print("我是毛胚房......")
horse() #输出我是毛胚房...... ------>刷漆、铺地板 ------装修完成
如果不使用装饰器,则只能修改函数名,例如
def horse1(): 不可取,更改了函数名
house() #修饰house
print("刷漆、铺地板")
此时,更改了函数名,程序中出现大量函数名,所以此方法不可取
作为参数的 函数中带有参数
#登录校验
import time
def decorate2(func):
def wrapper(*args, **kwargs): #一般装饰器都会加上*args,**kwargs,接收所有元素及字典
print("正在校验中......")
time.sleep(2)
print("校验完毕......")
print("测试 --{}".format(kwargs))
func(*args, **kwargs) #调用原函数 args——>元组 *args——>拆元组成每个元素
return wrapper
@decorate2
def f1(n):
print("---f1---", n)
注:args ——> 元组()
kwargs ——> 字典{}
*args ——> 将元组中所有元素拆分
**kwargs ——> 将字典中所有键值对拆分
加上装饰器后,自定义函数有参数:
@decorate2
def f2(students):
for stu in students:
print(stu)
f1(5) #调用的是decorate2中的wrapper代码
list1 = ["name1", "name2", "name3", "name4", "name5"]
f2(list1) #此时f1是wrapper
@decorate2
def f4(s_list, class_num = 5):
print("当前教室号码为:%s" % class_num)
for stu in s_list:
print("学生姓名为: %s" % stu)
f4(list1, class_num=10)
多层装饰器 靠近传参的函数的装饰器先执行
如果装饰器是多层的,谁距离函数最近就优先使用哪个装饰器
前一个装饰器执行完成后,将装饰器发送到下一个装饰器中
def zhuang1(func):
print("--->1 start")
def wrapper():
func()
print("刷漆")
print("--->1 end")
return wrapper
def zhuang2(func):
print("------>2 start")
def wrapper():
func()
print("铺地板、装门")
print("------>2 end")
return wrapper
@zhuang1
@zhuang2
def house():
print("毛坯房..")
house()
输出:
------>2 start
------>2 end
--->1 start
--->1 end
毛坯房..
铺地板、装门
刷漆
带参数的装饰器 在原装饰器的基础上增加一层接收参数
带参数的装饰器是三层的
最外层的函数负责接收装饰器参数
里面的内容还是原装饰器的内容
def outer(a): #第一层 负责接收参数的
def decorate_args(func): #第二层 负责接收函数的
def wrapper(*args, **kwargs): #第三层 负责接收函数的参数
func(*args)
print("--->铺地砖{}块".format(a))
return wrapper #返出来的是第三层
return decorate_args #返出来的是第二层
@outer(10)
def cus_house(time1):
print("我{}日期拿到房子的钥匙,是毛坯房".format(time1))
cus_house("2020-03-23")
@outer(1000)
def street(name):
print("新修的街道名字是:{}".format(name))
street("中山路")
输出:
我2020-03-23日期拿到房子的钥匙,是毛坯房
--->铺地砖10块
新修的街道名字是:中山路
--->铺地砖1000块
例子:
#例子开发:登录验证 登录后付款
import time
islogin = False #默认是没有登录的
#定义一个登录函数
def login():
username = input("输入用户名:")
password = input("输入密码:")
if username == 'admin' and password == '123':
return True
else:
return False
#定义一个装饰器,进行付款验证
def login_required(func):
def wrapper(*args, **kwargs):
global islogin
#验证用户是否登录
print("——————正在进行付款操作")
if islogin:
func(*args, **kwargs)
else:
#跳转到登录页面
print("用户未登录,不能付款")
islogin = login()
print("result", islogin)
return wrapper
@login_required
def pay(money):
print("正在付款,付款金额是:{}元".format(money))
print("付款中...")
time.sleep(2)
print("付款完成!")
#调用
pay(10000)
pay(8000)
输出:
——————正在进行付款操作
用户未登录,不能付款
输入用户名:admin
输入密码:123
result True
——————正在进行付款操作
正在付款,付款金额是:8000元
付款中...
付款完成!
四、总结
函数作用域 LEGB
L:Local 本地 局部变量
E:Encloseing 嵌套
G:Global 全局
B:Built-in 内置的
嵌套函数:内层函数、外层函数
闭包特点:
1、内层函数
2、内层函数引用外层函数的变量
3、返回内层函数
装饰器
除闭包的特点外,还有4、函数作为外层函数的参数
使用装饰器
@函数名
def 函数名():
pass
装饰器带参数 @函数名(a)
装饰器函数为嵌套三层