📚 Python 进阶系列 | 第一篇
基础语法学完了,这篇带你进入 Python 的高级特性。掌握这些,你的 Python 水平将上一个台阶!
一、浅拷贝与深拷贝
面试必问!搞不清楚浅拷贝和深拷贝的区别,面试基本就挂了。
问题场景
list1 = [1, 2, 3, [100, 200, 300]]list2 = list1.copy()# 浅拷贝
浅拷贝:只拷贝"第一层"
list1 = [1, 2, 3, [100, 200, 300]]list2 = list1.copy()# 修改第一层元素:互不影响list1[0] = 10print(list2[0])# 1(不受影响)# 修改嵌套的可变对象:两边都变!list1[3].append(400)print(list2[3])# [100, 200, 300, 400](被影响了!)
💡 浅拷贝只拷贝了"第一层",嵌套的子对象还是共享的。
浅拷贝的三种方式:
list2 = list1.copy() # 方法一list2 = list1[:] # 方法二:切片list2 = list(list1) # 方法三:工厂函数
深拷贝:完全独立
import copylist1 = [1, 2, 3, [100, 200, 300]]list3 = copy.deepcopy(list1) # 深拷贝list1[3].append(400)print(list3[3]) # [100, 200, 300](完全不受影响!)
💡 深拷贝会递归地拷贝所有层级的对象,两边完全独立。
拷贝的特殊情况
# 非容器类型(数字、字符串)无法拷贝,始终指向同一对象var1 = 1var2 = copy.copy(var1)var3 = copy.deepcopy(var1)print(id(var1) == id(var2) == id(var3))# True# 元组如果只包含不可变对象,深拷贝也不会创建新对象tup1 = (1, 2, 3)tup2 = copy.deepcopy(tup1)print(id(tup1) == id(tup2)) # True# 但如果元组包含可变对象,深拷贝会创建新对象tup1 = (1, 2, 3, [10, 20])tup2 = copy.deepcopy(tup1)print(id(tup1) == id(tup2)) # False
三种拷贝方式对比
| | | |
|---|
| b = a | | |
| b = a.copy() | | |
| b = copy.deepcopy(a) | | |
二、迭代器
什么是可迭代对象?
能用 for 循环遍历的对象都叫可迭代对象(Iterable):
for element in [1, 2, 3]: # 列表 print(element)for element in (1, 2, 3):# 元组 print(element)for key in {”one”: 1, ”two”: 2}:# 字典 print(key)for char in ”123”: # 字符串 print(char)for line in open(”file.txt”): # 文件 print(line)
Iterable vs Iterator
from collections.abc import Iterable, Iterator# 可迭代对象:能用 for 遍历isinstance([], Iterable)# Trueisinstance((), Iterable)# Trueisinstance({}, Iterable)# Trueisinstance(”abc”, Iterable)# True# 迭代器:能用 next() 取值isinstance([], Iterator) # False ← 列表不是迭代器!isinstance((), Iterator) # Falseisinstance(”abc”, Iterator) # Falseisinstance((x for x in range(10)), Iterator) # True ← 生成器是迭代器
iter() 和 next()
list1 = [1, 2, 3]# 用 iter() 创建迭代器it = iter(list1)print(type(it)) #<class 'list_iterator'># 用 next() 逐个取值print(next(it))# 1print(next(it))# 2print(next(it))# 3# print(next(it)) # ❌ StopIteration(没有更多元素了)
💡for 循环的工作原理:
- 当
next() 抛出 StopIteration 时,循环结束
自定义迭代器:反转列表
from typing import Iterable, Iteratorclass Reverse: def __init__(self, data): self.data = data self.index = len(data) def __iter__(self): return self def __next__(self): if self.index == 0: raise StopIteration else: self.index -= 1 return self.data[self.index]rev = Reverse([1, 3, 5, 7, 9])print(isinstance(rev, Iterable))# Trueprint(isinstance(rev, Iterator))# Trueprint(next(rev))# 9print(next(rev))# 7
三、生成器
什么是生成器?
生成器是一种特殊的迭代器,用函数定义,用 yield 返回值。
优势:按需生成数据,避免一次性生成大量数据占用内存。
用生成器表达式创建
# 列表推导式:一次性生成所有数据,占内存nums = [i**2 for i in range(1000000)]# 生成器表达式:按需生成,几乎不占内存nums = (i**2 for i in range(1000000))
用函数创建生成器
def fibo(): a, b = 0, 1 while True: yield b a, b = b, a + bf = fibo()print(type(f)) # <class 'generator'>print(next(f)) # 1print(next(f)) # 1print(next(f)) # 2print(next(f)) # 3print(next(f)) # 5
yield vs return
return:返回值后函数结束yield:返回值后函数暂停,下次调用从暂停处继续
def count_up(n): i = 0 while i <= n: yield i i += 1for num in count_up(5): print(num) # 0, 1, 2, 3, 4, 5
获取生成器的返回值
def fibo(): a, b, counter = 0, 1, 0 while counter < 10: yield b a, b, counter = b, a + b, counter + 1 return ”done” # 返回值f = fibo()try: while True: print(next(f))except StopIteration as e: print(e.value) # ”done”
💡 生成器的返回值藏在 StopIteration 异常的 value 属性中。
send 方法:向生成器发送数据
def gen(): task_id = 0 int_value = 0 char_value = ”A” while True: match task_id: case 0: # yield 产出值,send 送入的值赋给 task_id task_id = yield int_value int_value += 1 case 1: task_id = yield char_value char_value = chr(ord(char_value) + 1) case _: yieldf = gen()print(f.send(None))# 0(先启动生成器)print(f.send(1))# Aprint(f.send(1))# Bprint(f.send(0))# 1
💡 send 可以向生成器发送数据,作为 yield 表达式的结果。
四、作用域(LEGB 规则)
Python 查找变量时遵循LEGB规则:
Local(局部)→ Enclosing(嵌套)→ Global(全局)→ Built-in(内建)
五种命名空间
嵌套作用域
a = 100# 全局def outer(): b = 200 # 外层函数的局部变量 def inner(): c = 300 # 内层函数的局部变量 print(c)# 300 print(b)# 200 print(a)# 100 return innerf = outer()f()
五、闭包
什么是闭包?
闭包满足三个条件:
def Outer(a, b): def inner(x): return a * x + b # 访问外层的 a 和 b return innerinn = Outer(1, 2)print(inn(2)) # 4 (1*2+2)print(inn(3)) # 5 (1*3+2)
查看闭包的自由变量
# __closure__ 属性包含自由变量的单元格对象for cell in inn.__closure__: print(cell.cell_contents) # 1, 2
💡 闭包的威力:外层函数的局部变量在函数返回后仍然"存活",被内层函数引用。
六、装饰器
什么是装饰器?
装饰器允许不修改原函数代码,就能增加新功能。
本质:接收一个函数作为参数,返回一个新的包装过的函数。
基础装饰器
from math import sqrt# 增加求绝对值的功能def get_abs(f): def inner(n): n = abs(n) return f(n) return inner# 转换成整数def get_int(f): def inner(n): return int(f(n)) return inner@get_int@get_absdef func(n): return sqrt(n)print(func(-4)) # 2(先取绝对值 4,再开方 2.0,再转 int 2)
💡 @装饰器名 是语法糖,等价于 func = get_int(get_abs(func))
多层装饰器
多个装饰器的装饰过程:离函数最近的装饰器先装饰,然后外面的装饰器再进行装饰。
@get_int # 第二层装饰@get_abs # 第一层装饰(先执行)def func(n): return sqrt(n)
带参数的装饰器
def times(count): def get_abs(f): def inner(n): n = abs(n) for i in range(count): n = f(n) return n return inner return get_abs@times(2)# 等价于 func = times(2)(func)def func(n): return sqrt(n)print(func(-16)) # 先 abs(-16)=16, sqrt(16)=4.0, sqrt(4.0)=2.0
💡 @times(2) 本质是 times(2)(func),返回一个两层嵌套的装饰器。
类装饰器
from math import sqrtclass DecoratorClass: def __init__(self, f): self.f = f # 接收被装饰的函数 def __call__(self, x): x = abs(x) # 增加功能 return self.f(x)@DecoratorClass # 等价于 func = DecoratorClass(func)def func(n): return sqrt(n)print(func(-4)) # 2.0
💡 类装饰器的原理:
@类名会进行类的实例化,将被装饰函数传给 __init__
本篇小结
| |
|---|
| 只拷贝第一层,子对象共享。copy() / [:] / list() |
| 递归拷贝所有层,完全独立。copy.deepcopy() |
| 能用 for 遍历。isinstance(x, Iterable) |
| 能用 next() 取值。实现 __iter__ 和 __next__ |
| 用 yield 返回值的特殊迭代器,按需生成,节省内存 |
| |
| |
| |
| |
| |
📝 学习建议:迭代器和生成器是 Python 高级特性的基础,装饰器是框架开发的核心。建议多动手写,特别是自定义装饰器和生成器。
觉得有用?点个「在看」让更多人看到 👇
有问题欢迎留言,我会一一回复!