大家好,我是 亦难
昨天我们用面向对象思想把“学生报名”和“小明减肥”两个场景全部代码化,今天继续进阶,一次性搞定 闭包、装饰器、深浅拷贝 这三件让新手头秃的“高阶三剑客”。读完你能:

① 用闭包把函数变量“续命”;
② 用装饰器给任何函数零侵入加功能;
③ 一眼看穿深浅拷贝的内存陷阱。


1️⃣ 闭包:让局部变量“长生不老”

作用

普通函数调用完就销毁局部变量,闭包能把变量“锁”在内存里,继续累加或做后续操作。

构成条件(一句话背下来)

有嵌套、有引用、有返回

最小可运行示例

def func_out(num1):
    def func_inner(num2):
        return num1 + num2      # 内部函数引用外部变量
    return func_inner           # 返回内部函数名

add_10 = func_out(10)          # 闭包实例
print(add_10(1))               # 11
print(add_10(2))               # 12

nonlocal:在闭包里修改外部变量

def counter():
    count = 0
    def incr():
        nonlocal count
        count += 1
        return count
    return incr

c = counter()
print(c(), c(), c())  # 1 2 3

2️⃣ 装饰器:零侵入给函数加外挂

作用

不改动原函数源码的前提下,动态增加功能(日志、计时、权限校验等)。

构成条件(升级版闭包)

有嵌套、有引用、有返回、有额外功能

语法糖写法(最常用)

import time

def timing(fn):
    def inner(*args, **kwargs):
        start = time.time()
        result = fn(*args, **kwargs)
        print(f"{fn.__name__} 耗时 {time.time()-start:.4f}s")
        return result
    return inner

@timing
def slow_add(a, b):
    time.sleep(0.5)
    return a + b

print(slow_add(1, 2))
# 输出:slow_add 耗时 0.5003s
#       3

万能模板(通用装饰器)

from functools import wraps

def universal_decorator(func):
    @wraps(func)                    # 保留原函数元数据
    def wrapper(*args, **kwargs):
        # 前置逻辑
        print("前置处理")
        result = func(*args, **kwargs)
        # 后置逻辑
        print("后置处理")
        return result
    return wrapper

3️⃣ 多个装饰器 & 带参装饰器

多个装饰器堆叠

执行顺序:离函数最近的先装饰、由内到外

@check_login
@check_code
def comment():
    print("发表评论")

等价于

comment = check_login(check_code(comment))

带参装饰器(三层嵌套)

def log(flag):                 # 第 1 层收参数
    def decorator(fn):         # 第 2 层收函数
        def inner(*a, **k):    # 第 3 层收实参
            print(f"[{flag}] 开始执行")
            return fn(*a, **k)
        return inner
    return decorator

@log("DEBUG")
def add(a, b):
    return a + b

print(add(1, 3))
# [DEBUG] 开始执行
# 4

4️⃣ 深浅拷贝:一张图看懂内存陷阱

场景 图示 结果
赋值 a = b 同一对象,同 id
浅拷贝 copy.copy() 外层新对象,内层引用
深拷贝 copy.deepcopy() 全部层级全新对象

代码验证

import copy

lst1 = [[1, 2], 3]
lst2 = copy.copy(lst1)      # 浅拷贝
lst3 = copy.deepcopy(lst1)  # 深拷贝

lst1[0].append(999)
print(lst2)  # [[1, 2, 999], 3]  内层被改
print(lst3)  # [[1, 2], 3]       完全独立

5️⃣ 今日实战:日志装饰器 + 深拷贝防御性编程

需求:给任意函数加日志,同时把可变参数深拷贝一份防止原数据被误改。

import copy, functools, time

def safe_log(flag):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            # 深拷贝可变参数
            cp_args = copy.deepcopy(args)
            cp_kwargs = copy.deepcopy(kwargs)

            start = time.time()
            result = func(*cp_args, **cp_kwargs)
            print(f"[{flag}] {func.__name__} 耗时 {time.time()-start:.4f}s")
            return result
        return wrapper
    return decorator

@safe_log("API")
def risky_process(data):
    data.append("修改")
    return data

lst = [1, 2, 3]
print(risky_process(lst))        # 返回新数据
print(lst)                       # 原数据保持不动


🚩 明日预报

《从三次握手到聊天室:Python 网络编程 30 分钟上手》
我们将用 socket 手写 TCP 客户端 + 服务端,带你跑通 三次握手、四次挥手、端口复用、粘包拆包 全套流程,明晚 21:00 见!

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐