Python基础语法入门(第十四天)——函数(三)之闭包与装饰器

闭包是指在函数内部定义并能访问外部函数变量的函数,常用于实现高级特性如装饰器、生成器。装饰器是闭包的一种应用,可以在不修改函数源代码的情况下添加额外功能。装饰器可以通过闭包调用法或Python的语法糖(@装饰器名)进行使用,支持链式使用多个装饰器。当需要装饰器带参数时,可以结合闭包的特点设计装饰器结构。
摘要由CSDN通过智能技术生成

定义

**闭包是指在函数内部定义的函数,并且该函数内部可以访问外部函数的变量。**具体来说,当一个内部函数引用了外部函数的变量时,即使外部函数已经执行完毕,这些变量仍然会被保留在内存中。

Python中的闭包通常用于实现一些高级特性,如装饰器、生成器等。

闭包有以下特点:

1.内部函数可以访问外部函数的变量:闭包中的内部函数可以引用外部函数中定义的变量,即使外部函数已经执行完毕,这些变量仍然会保留在内存中。
2.外部函数返回内部函数:通常情况下,外部函数会将内部函数作为返回值返回给调用者,从而形成一个闭包。
3.闭包保持了对外部变量的引用:由于闭包中的内部函数引用了外部变量,所以这些外部变量不会被垃圾回收机制回收,而是保存在闭包中。

一、闭包

在Python中,闭包就是指一个函数(内部函数)能够引用其外部作用域中定义的非全局变量。简单来说,闭包就是由函数及其相关的引用环境组合而成的实体。

1.1 闭包的构成条件
1. 函数嵌套
2. 内部函数能够使用外部函数的变量(包括外部函数接收的参数)
3. 外部函数返回内部函数的引用。

调用和引用

从代码书写格式来看,函数调用就是在函数名后加上括号(),引用就是不加括号;函数调用时会立即执行函数,而引用时不会执行。

从深层次来看,函数引用实际上就是将这个函数的内存地址赋给了某个变量。这样就可以通过某个变量来调用这个函数了。如下:

def say_hello():
    print("Hello, World!")

# 将say_hello函数赋值给greeting变量
greeting = say_hello

# 调用greeting变量所引用的函数
greeting()

在上面的例子中,我们定义了一个名为say_hello的函数,然后将其赋值给了greeting变量。接着,我们通过调用greeting()来调用被引用的say_hello函数。

需要注意的是,在赋值过程中不要加上括号,因为加上括号会直接执行该函数并将结果赋给变量。而我们想要的是将整个函数作为对象进行传递和操作。

1.2 闭包的使用

案例一:小红询问小美到了什么地方

步骤:

  • 定义外部函数,外部函数需要接收发送方参数(姓名)
  • 定义内部函数,接收回答方参数(姓名)
  • 将对话信息进行拼接
def outer(name):
    # 定义内部函数,参数是 说话的信息
    print(f'{name}:到北京了吗?')
 
    def inner(name1):
        # 内部函数中,将name和info进行拼接输出
        print(f'{name1}:已经到了,放心吧。')
 
    # 外部函数返回内部函数的地址
    return inner


func = outer('小红')
func('小美')

案例二:两数相加

def outer_function(x):
    def inner_function(y):
        return x + y
    return inner_function

closure = outer_function(10)
print(closure(5))  # 输出15

在上面的例子中,我们定义了一个名为say_hello的函数,然后将其赋值给了greeting变量。接着,我们通过调用greeting()来调用被引用的say_hello函数。

需要注意的是,在赋值过程中不要加上括号,因为加上括号会直接执行该函数并将结果赋给变量。而我们想要的是将整个函数作为对象进行传递和操作。

1.3 闭包修改外部函数变量

在函数内部是不能直接对外部变量进行修改的(不论是否是闭包),如果在函数内部要修改全局变量的话需要使用到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()

在上方代码中,内部函数中num在使用nonlocal声明之前进行赋值100,世纪上是对变量num的重新声明,同时其作用域也仅仅是在内部函数中。

二、装饰器

装饰器的本质就是一个闭包函数,能够在不修改已有函数(被装饰函数)源代码的情况下对函数进行额外功能的添加或修改其原有的功能。

装饰器本身是一个高阶函数,能够接收一个函数作为参数,并且返回一个新的函数。这个新的函数在调用原有的函数之前或之后会执行一些额外的函数,或者替换原有函数的实现。

2.1 装饰器的结构

通过上方的定义我们知道装饰器本质上就是闭包,那么在实现的时候首先要考虑的就是闭包的结构如何去实现,如回家时密码门需要验证密码,代码示例如下:

def login_check(fn):
    def inner():
        print("密码验证进行中....")
        fn()
    return inner


def home():
    print("欢迎回家!!!")

上方代码给出了两个函数,其中home函数就是主函数,要回家首先要验证密码,而login_check函数就是验证密码的函数,也就是说login_check函数需要在home函数执行前执行,同时根据其执行的结果来决定home函数是否要正常执行,假设密码为123,上方代码可修改为如下:

def login_check(fn):
    password = input("请输入密码:")
    def inner():
        print("密码验证进行中....")
        nonlocal password
        if password == "123":
            fn()
        else:
            print("对不起,密码输入错误!")
    return inner


def home():
    print("欢迎回家!!!")
2.2 装饰器的使用

装饰器定义好之后,如果作用于指定的函数呢?

1.闭包调用法

首先,装饰器的本质还是函数,所以直接调用,然后将home函数作为参数传递到装饰器中,外部函数调用了之后返回了内部函数的调用,因此再添加一个括号就可,调用格式为:login_check(home)(),完整代码如下:

def login_check(fn):
    password = input("请输入密码:")
    def inner():
        print("密码验证进行中....")
        nonlocal password
        if password == "123":
            fn()
        else:
            print("对不起,密码输入错误!")
    return inner


def home():
    print("欢迎回家!!!")

login_check(home)()

执行结果:

在这里插入图片描述

除此外,使用更多的还是语法糖的形式。

语法糖调用

装饰器的语法糖用法是在被装饰函数上按@装饰器名的格式来对被装饰函数进行装饰,如下:

def login_check(fn):
    password = input("请输入密码:")
    def inner():
        print("密码验证进行中....")
        nonlocal password
        if password == "123":
            fn()
        else:
            print("对不起,密码输入错误!")
    return inner


@login_check
def home():
    print("欢迎回家!!!")


home()

使用语法糖的时候直接调用被装饰函数即可,效果与闭包调用的时候是一样的。

多个装饰器的使用

def login_check(fn):
    password = input("请输入密码:")
    def inner():
        print("密码验证进行中....")
        nonlocal password
        if password == "123":
            print("密码验证成功!")
            fn()
        else:
            print("对不起,密码输入错误!")
    return inner


def face_check(fn):
    def inner():
        print("记得换鞋哦~")
        fn()
    return inner


@login_check
@face_check
def home():
    print("欢迎回家!!!")


home()

多个装饰器装饰同一个函数,装饰顺序是就近原则,谁离原函数近,就先装饰谁。

思考:装饰器要传参的话应如何实现?(结合闭包的特点来思考)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

quanmoupy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值