---
id: 200089
title: "学习进展#2"
slug: 200089
status: publish
format: markdown
cats: ["文章栏目", "Python"]
tags: ["Python", "学习笔记"]
created: 2026-10-09 17:39:14
updated: 2026-10-09 18:32:59
author: 星汐梦隐
url: https://blog.xxmy.work/200089.html
---

# 闭包和装饰器与深浅拷贝
## 闭包
* **定义** :内部函数使用了外部函数的变量
* **作用** :用于延长函数内变量的生命周期 ,或编写装饰器
* **语法** :
```python
# 外部函数
def 外部函数名(外部参数):
    外部函数(局部)的变量
	# 内部函数
	def 内部函数名(内部参数):
			...[使用外部函数的变量]
	return 内部函数名	# 外部函数的返回值
```

**构成条件** :

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

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

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

例子:

```python
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**关键字

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

  ```python
  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. **有额外功能**:给需要装饰的原有函数增加额外功能

* **定义语法** :

```python
def check(fn_name): #fn_name是一个函数名
    def fn_inner(): #定义内部函数
        print("检查用户是否登录")   #添加额外功能
        fn_name()
    return fn_inner	#返回内部函数名
```

* **调用语法**:

```python
# 格式1:传统写法
	装饰后的函数名 = 装饰器名(被装饰的原函数名)
    装饰后的函数名()
# 格式2:语法糖
	#在要被装饰的原函数前,直接写 @装饰器名,之后直接调用函数即可
--------------------------------
#传统方式
comment = check(comment)
comment()
# 语法糖
@check
def pay():
    print("充值中...")
pay()
```

* **多次修饰**:一个函数可以挂任意多个装饰器,一般**装饰自由内到外、** ***执行自上而下(语法糖)***

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

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

```python
# 可变函数的装饰器用法
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__ == "函数名"`的方法判断函数

```python
# 定义含参装饰器(就是嵌套装饰器)
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. 如果是针对于不可变类型,则用法和普通赋值一样,并无区别

```python
import copy	#导包
```
```python
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]]
    # 普通赋值就是把变量的地址给新变量(类似于引用)
    #原理:可变变量是内存中新开辟的地址
```

```python
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的地址]
    #原理:拷贝到地址,修改(指向地址的变量)就会对原变量产生影响.
```

```python
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
    #只能重新赋值,不能修改
```

```python
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]]
```

```python
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. **不可变类型同赋值,不开辟空间,不能更改内容(只能重新赋值).**
	1. **可变类型赋值时耦合性最高,浅拷贝多层才耦合,深拷贝不耦合.**

