Python的闭包与装饰器

闭包

函数

函数名的作用

  • 函数名存放的是函数所在内存空间的地址(即:函数名代表函数入口地址
  • 函数名()执行函数名所存放空间地址中的代码(即:函数名()代表函数调用:直接调用)
  • 函数名可以像普通变量一样赋值,赋值后的结果与原函数名作用是一样的(即:函数名可以做函数参数函数名做函数参数是研究的重点
函数作为对象,可以赋值
# 定义无返回值函数
def  fun01():
	print("hello world")
	
print(fun01)   # 打印函数的地址值
print(fun01())  # 先调用函数,然后再打印fun01函数中的return内容;由于此函数无返回值,故打印None
f2 = fun01  # 相当于把fun01的地址赋值给了f2
print(f2)  # 结果与print(fun01)一样
print(f2)   # 结果与print(fun01())一样
函数作为(实际)参数使用,可以传入函数

函数名作为实参传递,本质上传递的是对应函数的地址

# 1.定义普通函数method(),输出内容
def method():
	print("---我是 method 函数")
	
# 2.定义func()函数,接收1个参数(函数对象),然后调用 该函数即可
def func(fn_name):
	'''
	该函数用于接收1个函数对象,然后再内部 调用此函数
	:param  fn_name:接收到的函数名,充当:实参(对象)
	:return:无
	'''
	fn_name

闭包

概念与作用

闭包可以保存函数内的变量,而不会随着调用完函数而被销毁。

在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,这种:使用外部函数变量的内部函数称为闭包

image-20250402192922752

构成条件

  • 1.有嵌套:在函数嵌套(函数里面再定义函数)的前提下;
  • 2.有引用:内部函数使用了外部函数的变量(还包括外部函数的参数);
  • 3.有返回:外部函数返回了内部函数名。
定义一个用于求和的闭包
其中,外部函数有参数num1,内部函数有参数num2,然后调用,并用于求解两数之和
# 闭包的构成条件:
# 1.在函数嵌套(函数里面再定义函数)的前提下
def func_out(num1):
	def func_inner(num2):
# 2.内部函数使用了外部函数的变量(还包括外部函数的参数)
		num = num1 + num2
		print("现在的值:",num)
#3.外部函数返回了内部函数名
	return func_inner
	
	
	
# 创建闭包实例
f = func_out(10)      # f = func_out函数的返回值  = inner这个内部函数对象  这行代码走完,外部函数就执行结束了

执行闭包
f(1)

nonlocal关键字

这是1个关键字,可以实现 让内部函数 去修改 外部函数的变量值。

global:声明变量 为全局变量

nonlocal:声明 外部函数的变量值 可以被 内部函数修改

# 需求: 编写1个闭包, 让内部函数去访问外部函数的参数a = 100, 并观察效果.

# 1. 闭包写法, 实现上述需求.
def fun_outer():                    # 有嵌套
    a = 100     # 外部函数的 变量

    # 定义内部函数, 去访问, 并修改外部函数的变量.
    def fun_inner():                # 有嵌套
        # 核心细节: 在内部函数中修改外部函数的变量值, 要通过 nonlocal关键字实现.
        nonlocal a
        a = a + 1
        print(f'a的值为: {a}')       # 有引用

    return fun_inner                # 有返回


# 2. 调用函数.
# 2.1 调用外部函数, 获取其返回值, 即: 内部函数对象
fn = fun_outer()        # fn = fun_inner

# 2.2 调用"内部函数"
fn()   # 101
fn()   # 102
fn()   # 103

装饰器

装饰器的作用

装饰器的作用是:不改变原有函数的基础上,给原有函数增加额外功能

装饰器本质上就是一个闭包函数

构成条件

  • 1.有嵌套:在函数嵌套(函数里面再定义函数)的前提下;
  • 2.有引用:内部函数使用了外部函数的变量(还包括外部函数的参数);
  • 3.有返回:外部函数返回了内部函数名;
  • 4.有额外功能:给需要装饰的原有函数增加额外功能 。

语法

方式一:传统方法: 变量名 = 装饰器名(原有函数名)

​ 变量名() # 执行的就是 装饰后的 原函数

方式二:语法糖(语法格式简化版):@装饰器名 (这种方式最常见也最常用)

细节:

​ 内部函数的形式,必须和 原函数(要被装饰的函数)形式一致,即:要有参数都有参数,要有返回值就都有返回值

装饰不定长函数

"""
细节:
    装饰器的内部函数 格式必须和 (要被装饰的)原函数 格式保持一致, 即: 要么都是无参无返回, 要么都是无参有返回......
"""

# 案例: 演示装饰器 装饰 有参(可变类型)有返回值的函数.

# 需求: 定义函数 get_sum(), 用于计算多个数据和.  在不改变其源码的情况下, 实现: 添加一个友好提示.
# 1. 编写装饰器.
def print_info(fn):                                 # 有嵌套
    # 定义内部函数, 其格式必须和 原函数 格式保持一致.
    def inner(*args, **kwargs):                     # 有嵌套
        print('[友好提示] 正在努力计算中!')             # 有额外功能
        return fn(*args, **kwargs)
    return inner                                    # 有返回

# 2. 编写原函数: 有参(可变参数)有返回值.
@print_info
def get_sum(*args, **kwargs):
    """
    该函数用于计算 多个数据的 和.
    :param args: 可变参数, 会接收所有的位置参数, 然后放到元组中
    :param kwargs: 可变参数, 会接收所有的关键字参数, 然后放到字典中
    :return: 多个数据的求和 结果.
    """
    # 2.1 定义sum变量, 记录求和结果.
    sum = 0

    # 2.2 先计算所有位置参数(元组)的和.  例如: (1, 2, 3, 4, 5)
    for i in args:
        sum += i

    # 2.3 再计算所有关键字参数(字典)的和. 例如: {'a': 10, 'b': 20, 'c': 30}
    for value in kwargs.values():
        sum += value

    # 2.4 返回值最终的计算结果.
    return sum

# 3. 在main函数中进行测试.
if __name__ == '__main__':
    print(get_sum(1, 2, 3, 4, 5, a = 10, b = 20, c = 30))

多个装饰器装饰一个函数

​ 多个装饰器的装饰过程是:离函数最近的装饰器先装饰,然后外面的装饰器再进行装饰,由内到外的装饰过程

细节:

  • 1.多个装饰器,装饰1个函数,装饰的顺序是 由内向外的 即:传统写法
  • 2.但是多个装饰器的执行顺序是,由上往下的 即:语法糖写法
"""
细节(记忆):
    1. 多装饰器装饰1个原函数, 会按照由内到外的顺序进行装饰(即: 传统装饰写法).
    2. 但是你看到的执行顺序是: 从上往下(从外到内)的顺序执行的. (语法糖写法)
"""

# 需求: 发表评论前, 需要先登录, 再进行验证码验证.

# 1. 定义装饰器, 负责给原函数增加: 登陆功能.
def check_login(fn):
    def inner():                # 有嵌套
        print('登录中...')       # 有额外功能
        fn()                    # 有引用
    return inner                # 有返回

# 2. 定义装饰器, 负责给原函数增加: 验证码功能.
def check_code(fn):
    def inner():                # 有嵌套
        print('校验验证码...')    # 有额外功能
        fn()                    # 有引用
    return inner                # 有返回

# 3. 定义原函数.
# @check_login
# @check_code
def comment():
    print('发表评论!')

# 4. 测试
if __name__ == '__main__':
    # 方式1: 传统方式.
    # comment = check_code(comment)   # 增加验证码功能.
    # comment = check_login(comment)       # 增加登陆功能
    # comment()

    cc = check_code(comment)  # 增加验证码功能.
    cl = check_login(cc)       # 增加登陆功能
    cl()

    # 方式2: 装饰器语法糖写法.
    # comment()

带参数的装饰器

一个装饰器 装饰 多个原函数

使用带有参数的装饰器,其实是在装饰器外面又包裹了一个函数,使用该函数接收参数,返回装饰器

细节:

​ 1个装饰器 的参数, 只能有 1个

例如:需求:定义1个既能装饰 减法运算,又能装饰 加法运算 的装饰器

思路1:

# 1.定义装饰器,实现:传入add()函数,就提示 加法运算,传入substract()函数,就提示:减法运算
def logging(flag):    
    def print_info(fn_name): #这个才是我们的装饰器,因为1个装饰器的参数只能有1个,所以在其外边再包裹一层,专门用于传入:参数
        def inner(a,b):                  # 有嵌套
            if flag == '+':
                print('正在努力计算 加法 中...')   # 有额外功能
            elif flag == '-':
                print('正在努力计算中...')        # 有额外功能
            fn_name(a,b)                #有引用
        return inner 				 #有返回
	return print_info

# 2.定义函数add(),表示:加法运算
@logging('+')
def add(a,b):
	result = a + b
	print(result)

# 3.定义函数substract(),表示:减法运算
@logging('-')
def substract(a,b):
	result = a - b
	print(result)
	

# 在main函数中测试
if __name__=='__main__':
	# 4.调用add()函数,实现:计算两个整数和
	add(11,33)
	print('-' * 20)
	
	# 5.调用substract(),实现:计算两个整数差
	substract(100,20)

思路2:为了做题,即:实现这个需求

# 1.定义装饰器,实现:传入add()函数,就提示 加法运算,传入substract()函数,就提示:减法运算
def print_info(fn_name): 
	def inner(a,b):                  # 有嵌套
    	if fn_name == add:
        	print('正在努力计算 加法 中...')   # 有额外功能
        elif fn_name == substract:
            print('正在努力计算 减法 中...')        # 有额外功能
        fn_name(a,b)                #有引用
    return inner 				 #有返回
    
# 2.定义函数add(),表示:加法运算
@print_info
def add(a,b):
	result = a + b
	print(result)

# 3.定义函数substract(),表示:减法运算
@print_info
def substract(a,b):
	result = a - b
	print(result)
# 在main函数中测试
if __name__=='__main__':
	# 4.调用add()函数,实现:计算两个整数和
	add(11,33)
	print('-' * 20)
	
	# 5.调用substract(),实现:计算两个整数差
	substract(100,20)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值