跳到主要内容Python 核心技术点汇总:装饰器、拷贝与数据结构 | 极客日志Python算法
Python 核心技术点汇总:装饰器、拷贝与数据结构
Python 核心技术点涵盖装饰器原理与实现、深浅拷贝区别及场景、常见数据结构特性对比。内容包含列表去重技巧、递归与循环选用策略,以及 Python 2 与 3 版本关键差异。旨在帮助开发者巩固基础,理解底层机制,避免常见陷阱。
dehua dong1 浏览 Python 装饰器
一、什么是装饰器?
装饰器在 Python 中扮演着'化妆师'的角色,它能在不修改原函数代码的前提下,给函数动态添加新功能。
- 本质:一个接收函数作为参数,并返回新函数的工具。
- 作用:就像给手机贴膜,既保护屏幕(原函数),又新增防摔功能(装饰逻辑)。
二、核心原理
- 函数是对象:Python 中函数可以像变量一样传递,这是装饰器的基础。
- 闭包机制:装饰器通过嵌套函数(闭包)保留原函数,并包裹新功能。
工作流程:
- 调用被装饰的函数(如
hello())。
- Python 实际执行的是装饰器加工后的新函数。
- 新函数先执行装饰器添加的逻辑(如权限检查),再执行原函数。
三、常见用途
| 场景 | 作用 | 生活类比 |
|---|
| 权限验证 | 检查用户是否登录再执行函数 | 进小区前刷卡(门禁系统) |
| 日志记录 | 自动记录函数调用时间和参数 | 飞机黑匣子(自动记录飞行数据) |
| 性能统计 | 计算函数运行耗时 | 跑步时用手表计时 |
| 缓存结果 | 避免重复计算(如 @lru_cache) | 备忘录(记下答案直接复用) |
四、两种实现方式
- 函数式装饰器
最常用,通过嵌套函数实现。例如给函数添加'呼叫提醒'功能:
def remind_call(func):
def wrapper():
print("【提醒】开始打电话...")
func()
print("【提醒】通话结束")
return wrapper
@remind_call
def call_friend():
print("正在和好友通话中...")
call_friend()
【提醒】开始打电话...
正在和好友通话中...
【提醒】通话结束
- 类装饰器
通过实现
__call__ 方法让类能像函数一样调用。适合需要维护状态的场景(如重试计数器)。
五、需要注意的坑
-
原函数信息丢失
直接使用装饰器会导致 help(func) 显示的是 wrapper 函数的信息。解决方式是使用 functools.wraps 装饰 wrapper 函数。
-
装饰顺序影响
多个装饰器从下往上执行:
@decorator1
@decorator2
def func():
pass
- 带参数的装饰器
需要三层嵌套函数,最外层接收装饰器参数。
六、为什么用装饰器?
- 避免重复代码:如所有函数都要加日志时。
- 保持纯净:不修改原函数,降低耦合。
- 灵活组合:像乐高积木一样叠加功能。
一句话总结:装饰器是 Python 的'功能外挂',用 @ 符号轻松实现代码增强!
Python 的深拷贝和浅拷贝
1. 基本概念
| 类型 | 特点 | 适用场景 |
|---|
| 浅拷贝 | 只复制对象本身,不复制内部的子对象 | 简单对象(如列表嵌套不深) |
| 深拷贝 | 递归复制对象及其所有子对象 | 复杂嵌套对象(如多层字典/列表) |
2. 核心区别
- 浅拷贝:像给房子拍照片,只复制了外观(顶层结构),房间里的家具(子对象)还是同一套。
- 深拷贝:连房子带家具全部克隆一份,新旧对象完全独立。
3. 技术实现
import copy
a = [1, [2, 3]]
b = copy.copy(a)
- 效果:修改
a[0](不可变类型)不影响 b;但修改 a[1][0](可变子对象)会影响 b。
- 效果:无论修改
a 的哪一层,c 都完全不受影响。
4. 通俗例子
场景 1:合租公寓(浅拷贝)
你 (a) 和室友 (b) 共用客厅的冰箱(子对象)。你往冰箱里放啤酒 → 室友也能看到这些啤酒。但你换了自己的床单(顶层不可变项) → 室友的床单不变。
场景 2:买新房(深拷贝)
开发商按样板房 (a) 给你建了完全一样的房子 (c)。你在新房砸墙 → 样板房毫无影响。样板房换家具 → 你的新房也不变。
5. 验证实验
import copy
original = [1, {'name': 'Alice'}, [3, 4]]
shallow = copy.copy(original)
deep = copy.deepcopy(original)
original[1]['name'] = 'Bob'
original[2].append(5)
print("原始对象:", original)
print("浅拷贝:", shallow)
print("深拷贝:", deep)
6. 特殊注意事项
- 不可变类型:数字、字符串、元组深浅拷贝无区别(因为本身不能修改)。示例:
a = (1, [2]); b = copy.copy(a) → 修改 a[1] 仍会影响 b。
- 自定义对象:需实现
__copy__() 和 __deepcopy__() 方法控制拷贝行为。
- 性能权衡:深拷贝比浅拷贝慢,对复杂结构可能差 10 倍以上。
7. 什么时候用哪种?
- 用浅拷贝:数据没有嵌套或子对象不可变;需要节省内存/时间;明确希望共享子对象时。
- 用深拷贝:复杂嵌套结构且需要完全独立副本;防止意外修改影响原数据;需要序列化/反序列化时。
总结:浅拷贝是'省力但不彻底',深拷贝是'一劳永逸'。就像备份手机:浅拷贝像云同步(部分依赖原数据),深拷贝像整机克隆(完全独立)。遇到嵌套结构,当心浅拷贝的'连带影响'!
Python 数据结构与去重
一、内置数据结构
| 类型 | 特点 | 示例 |
|---|
| 列表 (list) | 有序、可变、允许重复 | [1, 2, 2, 3] |
| 元组 (tuple) | 有序、不可变、允许重复 | (1, "a", True) |
| 集合 (set) | 无序、可变、不允许重复 | {1, 2, 3} |
| 字典 (dict) | 键值对、键不可重复 | {"name": "Alice", "age": 20} |
| 字符串 (str) | 不可变字符序列 | "hello" |
二、List vs Set
| 对比项 | List | Set |
|---|
| 顺序 | 保持插入顺序 | 无序(存储顺序不确定) |
| 重复元素 | 允许重复 | 自动去重 |
| 查找速度 | 慢(遍历查找,O(n)) | 极快(哈希表实现,O(1)) |
| 适用场景 | 需要保留顺序或重复数据时 | 去重、快速成员检测 |
names = ["Alice", "Bob", "Alice"]
unique_names = {"Alice", "Bob"}
三、数组 (Array) vs 链表 (LinkedList)
| 对比项 | 数组 | 链表 |
|---|
| 内存分配 | 连续内存,大小固定 | 非连续内存,动态扩展 |
| 访问速度 | 极快(O(1),直接索引) | 慢(O(n),需遍历节点) |
| 插入/删除 | 慢(需移动元素,O(n)) | 快(O(1),修改指针即可) |
| 适用场景 | 频繁随机访问,数据量固定 | 频繁增删,数据量变化大 |
- 数组:像书架上的书,直接按编号拿(快),但插入新书要挪动其他书(慢)。
- 链表:像藏宝图,每步告诉你下一个地点在哪,添加新线索只需改箭头(快),但找宝藏要一步步走(慢)。
四、Python 中的实现
- 数组:使用
list(实际是动态数组,非严格数组)或 array 模块(类型受限但更省内存)。
import array
arr = array.array('i', [1, 2, 3])
- 链表:需手动实现或使用
collections.deque(双端队列,近似链表特性)。
class Node:
def __init__(self, val):
self.val = val
self.next = None
五、如何选择?
- 需要快速访问/修改? → 用列表(动态数组)
- 需要频繁去重/检测存在? → 用集合
- 需要高效插入/删除? → 考虑链表(但 Python 中优先用
list,除非极端性能需求)
总结:Python 的 list 和 set 分别解决顺序存储和快速查询的问题,而数组与链表的区别是内存结构的根本差异。
六、列表去重技巧
1. 使用 set() 去重(简单快捷,但会打乱顺序):
my_list = [1, 2, 2, 3, 1]
unique_list = list(set(my_list))
my_list = [1, 2, 2, 3, 1]
unique_list = []
seen = set()
for item in my_list:
if item not in seen:
seen.add(item)
unique_list.append(item)
3. 使用 dict.fromkeys()(保持顺序,适合 Python 3.7+):
my_list = [1, 2, 2, 3, 1]
unique_list = list(dict.fromkeys(my_list))
七、List、Vector、Map 区别
| 类型 | 描述 | 是否有序 | 是否可重复 | 常用语言中的名称 |
|---|
| list | 有序元素集合,支持增删改查 | ✅ | ✅ | Python list, Java List |
| vector | 动态数组,自动扩容,支持随机访问 | ✅ | ✅ | C++ vector, Java ArrayList |
| map | 键值对集合,key 唯一,用于快速查找 | ❌ | key❌ value✅ | Python dict, C++ map, Java HashMap |
- list 适合顺序存储,如游戏关卡列表、操作记录。
- vector 类似 list,但在 C++ 等语言中是支持自动扩容的动态数组,适合频繁插入、访问。
- map 适合按键快速查找数据,比如通过玩家 ID 查找昵称或背包。
- 玩家背包中的物品列表:用
list 或 vector 存储。
- 用来映射玩家 ID 到玩家数据:用
map(如 Python 中的 dict)。
- 排行榜查找前 100 名玩家分数:可以用
list 去重、排序后显示。
总结:去重方法要结合'是否保留顺序'来选,三种数据结构各有用途,理解它们的特性可以帮助你在实际开发或测试中选择合适的数据结构。
递归的使用策略
使用递归的主要原因是为了让代码更简洁、结构更清晰,尤其适合处理具有'自相似'特征的问题,比如树结构、分形结构、回溯搜索、数学归纳等场景。
递归是指一个函数调用自身来解决问题,适用于下面这些情况:
- 问题可以分解成规模更小的子问题;
- 子问题的结构与原问题相同(即自相似);
- 最终有一个明确的终止条件(递归出口)。
与 for 循环的对比
| 对比点 | for 循环 | 递归 |
|---|
| 代码结构 | 适合线性问题,步骤明确 | 适合分治、树形或图形问题 |
| 可读性 | 简单任务更直观 | 复杂结构更直观(如树的遍历) |
| 性能开销 | 更高效,无额外栈空间 | 每次调用会消耗栈空间 |
| 可维护性 | 复杂问题不易扩展 | 思路清晰,符合数学表达逻辑 |
| 错误易发点 | 循环条件控制 | 容易造成栈溢出、忘记递归出口 |
典型例子:树的遍历
假设你在游戏里写一个技能树系统,每个技能点可能有多个子技能。
def traverse(skill_node):
print(skill_node.name)
for child in skill_node.children:
traverse(child)
stack = [root]
while stack:
node = stack.pop()
print(node.name)
stack.extend(node.children)
递归的写法更短,更符合'每个技能点都做同样的事'的逻辑,可读性强,代码更贴近问题本身的结构。
什么时候选择递归?
- 操作树结构、图结构(如遍历、搜索);
- 问题天然有递归定义(如斐波那契、阶乘、汉诺塔);
- 需要进行回溯、分治、深度优先搜索等;
- 写算法时为了清晰表达思路。
注意
- 如果没有出口条件,容易栈溢出;
- 对性能敏感的程序中,可能需要用循环或栈代替递归(叫做尾递归优化或递归转迭代)。
总结:递归的好处是代码更简洁、逻辑更自然,特别适合解决自结构问题;但它在性能和稳定性上不如循环,需要谨慎使用。在实际工作中,如果功能简单、可控,推荐优先用 for;如果是多层结构、递归定义的问题,就大胆用递归。
Python 2 与 Python 3 的区别
Python2 和 Python3 的主要区别集中在语法、标准库、字符编码和底层实现等方面。Python3 是 Python 官方推荐使用的版本,Python2 已在 2020 年正式停止支持。
1. 打印语法的不同
2. 字符串的处理(编码)
- Python2:默认字符串是 ASCII 编码,
str 是字节串,unicode 是独立类型
- Python3:默认字符串是 Unicode 编码,
str 就是 Unicode,bytes 用于字节
3. 整数除法行为
- Python3:整数除法默认是真实除法(保留小数)
4. xrange 与 range
- Python2:
range 返回的是列表
xrange 返回的是生成器(节省内存)
- Python3:
5. input() 的行为
- Python2:
input() 会执行输入的表达式,容易有安全风险
raw_input() 才是读取字符串
- Python3:
6. 库兼容性
- Python3 的标准库进行了重构和改名,例如:
urllib → 拆分成 urllib.request, urllib.parse 等
Queue → queue
- 更统一的异常语法:
except Exception as e:
7. 其他特性差异(Python3 的优势)
- 支持更多高级语法,如:
f"" 格式化字符串
- 类型注解(type hints)
async/await 原生支持异步编程
- 更清晰的迭代器、生成器写法
- 更好的 Unicode 支持
- 更一致的语言设计理念(移除了旧的不一致用法)
总结对比表
| 功能/特性 | Python2 | Python3 |
|---|
| 打印语法 | print "abc" | print("abc") |
| 字符编码 | 默认 ASCII,需手动处理 Unicode | 默认 Unicode,字符串更统一 |
| 除法行为 | 5 / 2 = 2 | 5 / 2 = 2.5 |
| range/xrange | range 是列表,xrange 是生成器 | range 是生成器,没有 xrange |
| 输入函数 | raw_input() | input() |
| 官方支持 | 已停止支持 | 持续更新和优化 |
建议:如果你正在做任何新项目或测试框架搭建,一定要用 Python3。Python2 已经是历史版本,虽然一些老项目可能还在维护,但新系统已经不推荐再使用。
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- Gemini 图片去水印
基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
- curl 转代码
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online