Python(6)内部函数、闭包、装饰器

一、内部函数

内部函数特点:

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)

装饰器函数为嵌套三层

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值