下载

学习进展#2

2026-10-0934 次浏览0 条评论文章栏目

闭包和装饰器与深浅拷贝

闭包

  • 定义 :内部函数使用了外部函数的变量
  • 作用 :用于延长函数内变量的生命周期 ,或编写装饰器
  • 语法 :
# 外部函数
def 外部函数名(外部参数):
    外部函数(局部)的变量
    # 内部函数
    def 内部函数名(内部参数):
            ...[使用外部函数的变量]
    return 内部函数名    # 外部函数的返回值

构成条件 :

  1. 有嵌套: 在函数嵌套(函数里面再定义函数)的前提下
  2. 有引用: 内部函数使用了外部函数的变量(还包括外部函数的参数)
  3. 有返回: 外部函数返回了内部函数名

细节 :函数名 和 函数名() 是两个概念,前者表示函数对象,后者表示调用函数获取返回值

函数名可以赋值给变量,这个变量就是函数对象

例子:

def fun_outer(num1):
    def fun_inner(num2):
        sum = num1 + num2
        print(f"两个数的和为{sum}")
    return fun_inner
# 调用
fun = fun_outer(10)
fun(20)  # 输出: 两个数的和为30

fun_outer(10)(20)   #另一种调用方法

  • nonlocal关键字

  • 声明能够让内部函数去修改外部函数的变量

    def outer():
        num1 = 10
        def inner(num2):
            nonlocal num1  # 声明num1为nonlocal变量
            num1 += num2
            print(f"两个数的和为{num1}")
        return inner
    
    
    fun = outer()
    fun(20)  # 输出: 两个数的和为30
    fun(20)  # 输出: 50
    

装饰器

  • 在不改变原有函数的基础上,对其增加额外功能
  • 装饰器本质上就是一个闭包函数

  • 构成条件 :

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

def check(fn_name): #fn_name是一个函数名
    def fn_inner(): #定义内部函数
        print("检查用户是否登录")   #添加额外功能
        fn_name()
    return fn_inner #返回内部函数名
  • 调用语法:
# 格式1:传统写法
    装饰后的函数名 = 装饰器名(被装饰的原函数名)
    装饰后的函数名()
# 格式2:语法糖
    #在要被装饰的原函数前,直接写 @装饰器名,之后直接调用函数即可
--------------------------------
#传统方式
comment = check(comment)
comment()
# 语法糖
@check
def pay():
    print("充值中...")
pay()
  • 多次修饰:一个函数可以挂任意多个装饰器,一般装饰自由内到外、 执行自上而下(语法糖)

  • 装饰器的使用场景 :有(无)参有(无)返回值的原函数装饰、不定长参数原函数装饰

  • 注意 :装饰器的内部函数格式要和被装饰的原函数保持一致

# 可变函数的装饰器用法
def my_decorator(func):
    def inner(*args,**kwargs):
        print("正在执行求和操作")
        result = func(*args, **kwargs)
        return result
    return inner

@my_decorator
def get_sum(*args, **kwargs):
    """
    用于计算 数字元组 和 字典中数字的和
    :param *args: 数字元组,接收任意数量的位置参数 -> 元组
    :param **kwargs: 字典,key:字符串,value:数字,接收关键字参数 -> 字典
    """
    sum = 0
    for i in args:
        sum += i
    for v in kwargs.values():
        sum += v
    return sum

sum = get_sum(1,3,4,5,a=3,b=3)  # 输出: 正在执行函数前的操作
print(f"数字元组和字典中数字的和为{sum}")  # 输出: 数字元组和字典中数字的和为16
  • 装饰器含参时,常是嵌套装饰器.装饰器(本身)的参数有且只能有一个(即函数名,也可把函数名和参数封装成元组传入)
  • 也可通过 函数名.__name__ == "函数名"的方法判断函数
# 定义含参装饰器(就是嵌套装饰器)
def logging(flag):  #装饰器的参数,可以有多个参数
    def my_decorator(fun_name):
        def fun_inner(a,b):
            if flag == "+":
                print("正在计算[加法]中...")
            elif flag == "-":
                print("正在计算[减法]中...")
            return fun_name(a,b)
        return fun_inner
    return my_decorator

# 定义函数
@logging("+")
def jiafa(a,b):
    return (a+b)

@logging("-")
def jianfa(a,b):
    return (a-b)

深浅拷贝

  • 变量类型

    1. 不可变类型: 字符串,元组,整形,浮点型
    2. 可变类型: 字典,列表,集合(可变类型中内容的修改可能同时影响新旧变量)
  • 变量底层:

    1. 每个定量在内存中有固定地址存储,不可变类型变量的赋值操作就是把对应的值的地址给予变量,所以值不可修改(只能重新给变量赋值,例如原a=(1,2)重赋为a=(11,22))
    2. 可变类型变量的赋值操作是重新开辟一段内存存储,所以值可修改(能在不重新赋值的前提下,列如list[1]=10)
  • 概述:

    1. 所谓的深浅拷贝分别指的是copy模块的deepcopy()和copy()

    2. 深拷贝拷贝的多,浅拷贝拷贝的少

    3. 深浅拷贝主要是针对于可变类型来说的,深拷贝拷贝所有层(可变),浅拷贝只拷贝第1层(可变)

    4. 如果是针对于不可变类型,则用法和普通赋值一样,并无区别

import copy #导包
def dm01_普通赋值():
    # 普通赋值之 不可变类型
    a = 10
    b = a
    print('id(a)->', id(a))     # 0x01
    print('id(b)->', id(b))     # 0x01
    print('id(10)->', id(10))   # 0x01
    # 修改一个变量值只会对那个变量单独产生影响
    a = 20
    print('id(10)->', id(20))   #0x03
    print('id(a)->', id(a))     #0x03
    print('id(b)->', id(b))     #0x01
    print(a)    #20
    print(b)    #10
    # 原理:每个不可变变量的值都有固定的地址,对变量的赋值就是把这个变量指向对应值的地址
    # 普通赋值之 可变类型
    a = [1, 2, 3]
    b = [11, 22, 33]
    c = [a, b]
    d = c
    print('id(c)->', id(c)) #0x02
    print('id(d)->', id(d)) #0x02
    # 此时不论修改c还是d,两者都会变化.且地址不变
    c[1] = [2,41,5]
    print('id(c)->', id(c)) #0x02
    print('id(d)->', id(d)) #0x02
    print(c)    #[[1, 2, 3], [2, 41, 5]]
    print(d)    #[[1, 2, 3], [2, 41, 5]]
    # 普通赋值就是把变量的地址给新变量(类似于引用)
    #原理:可变变量是内存中新开辟的地址
def dm02_浅拷贝可变类型():
    a = [1, 2, 3]       #0x01
    b = [11, 22, 33]    #0x02
    c = [6, 7, a, b]    #0x03
    # 浅拷贝可变类型,开辟一份新内存真实拷贝一份(只拷贝第一层,有变量就拷贝地址)
    d = copy.copy(c)
    print('id(c)->', id(c)) #0x03
    print('id(d)->', id(d)) #0x04
    # 测试2
    print(id(c[2])) #0x01
    print(id(a))    #0x01
    # 修改
    a[2] = 22
    c[1] = 10
    print('c->', c) #[6,10,[1,2,22],[11,22,33]]
    print('d->', d) #[6,7,[1,2,22],[11,22,33]]
    # 浅拷贝只拷贝一层[6,7,a的地址,b的地址]
    #原理:拷贝到地址,修改(指向地址的变量)就会对原变量产生影响.
def dm03_浅拷贝不可变类型():
    # 不可变类型 a b c
    a = (1, 2, 3)
    b = (11, 22, 33)
    c = (6, 7, a, b)    #0x03
    # 浅拷贝不可变类型就是把两个变量名指向同一地址,不开辟内存
    d = copy.copy(c)
    print('id(c)->', id(c)) #0x03
    print('id(d)->', id(d)) #0x03
    #只能重新赋值,不能修改
def dm04_深拷贝可变类型():
    a = [1, 2, 3]       #0x01
    b = [11, 22, 33]    #0x02
    c = [6, 7, a, b]    #0x03
    # 深拷贝,开辟一份新的内存.连结构也拷贝
    d = copy.deepcopy(c)
    print('id(c)->', id(c)) #0x03->[6,7,0x01,0x02]
    print('id(d)->', id(d)) #0x04->[6,7,0x04,0x05]
    # 深拷贝直接拷贝所有层(不拷贝地址)
    a[1] = 100
    b[1] = 800
    print(f'c: {c}')    #[6,7,[1,100,3],[11,800,33]]
    print(f'd: {d}')    #[6,7,[1,2,3],[11,22,33]]
def dm05_深拷贝不可变类型():
    a = (1, 2, 3)       #0x01
    b = (11, 22, 33)    #0x02
    c = (a, b)          #0x03
    #深拷贝不可变类型
    d = copy.deepcopy(c)
    print(id(c))    #0x03
    print(id(d))    #0x03
    #同赋值
  • 总结:
    1. 不可变类型同赋值,不开辟空间,不能更改内容(只能重新赋值).
    2. 可变类型赋值时耦合性最高,浅拷贝多层才耦合,深拷贝不耦合.
0 条评论

发表评论

🔑