高级特性#
Python 提供了许多高级特性,如生成器、迭代器、推导式等,这些特性可以让代码更简洁、高效。
迭代器和可迭代对象#
可迭代对象与迭代器的关系#
| 类型 | 实现方法 | 特点 |
|---|---|---|
| 可迭代对象 | __iter__() | 可用 for 遍历,可多次迭代 |
| 迭代器 | __iter__() + __next__() | 可迭代对象的子集,一次性消耗,用完即止 |
迭代器一定是可迭代对象;可迭代对象不一定是迭代器(如 list 可多次迭代)。
可迭代对象(Iterable)#
可迭代对象是可以返回迭代器的对象,实现了 __iter__() 方法。
# 常见的可迭代对象
my_list = [1, 2, 3] # 列表
my_string = "hello" # 字符串
my_dict = {"a": 1} # 字典
my_set = {1, 2, 3} # 集合
my_tuple = (1, 2, 3) # 元组
# 检查是否为可迭代对象
from collections.abc import Iterable
print(isinstance(my_list, Iterable)) # True
# 使用iter()获取迭代器
iterator = iter(my_list)迭代器(Iterator)#
迭代器是实现了 __iter__() 和 __next__() 方法的对象。
# 手动迭代
my_list = [1, 2, 3]
iterator = iter(my_list)
print(next(iterator)) # 1
print(next(iterator)) # 2
print(next(iterator)) # 3
# print(next(iterator)) # StopIteration异常
# 自定义迭代器
class CountDown:
def __init__(self, start):
self.current = start
def __iter__(self):
return self
def __next__(self):
if self.current <= 0:
raise StopIteration
self.current -= 1
return self.current + 1
# 使用
for num in CountDown(5):
print(num) # 5, 4, 3, 2, 1生成器(Generator)(重点难点)#
生成器是一种特殊的迭代器,使用 yield 关键字创建。核心特性:惰性求值、按需生成、状态保持。
yield 执行流程#
调用 next(gen) → 执行到 yield → 暂停并返回值 → 再次 next() → 从暂停处继续yield暂停函数,将值返回给调用者- 下次
next()时从yield之后继续执行 - 函数结束时抛出
StopIteration(for自动处理)
生成器函数#
# 基本生成器函数
def countdown(n):
while n > 0:
yield n
n -= 1
# 使用生成器
for num in countdown(5):
print(num) # 5, 4, 3, 2, 1
# 手动调用
gen = countdown(3)
print(next(gen)) # 3
print(next(gen)) # 2
print(next(gen)) # 1
# next(gen) # StopIteration惰性求值与内存效率#
惰性求值:值在需要时才计算,不预先生成全部。生成器按需生成值,不需要一次性存储所有值。
# 普通函数(占用大量内存)
def squares_list(n):
result = []
for i in range(n):
result.append(i ** 2)
return result
# 生成器(内存效率高)
def squares_generator(n):
for i in range(n):
yield i ** 2
# 比较
list_squares = squares_list(1000000) # 返回列表,占用大量内存
gen_squares = squares_generator(1000000) # 返回生成器对象,几乎不占用内存
# 生成器耗尽后不可复用,需重新创建
gen = squares_generator(5)
list(gen) # [0, 1, 4, 9, 16]
list(gen) # [] 已耗尽生成器表达式#
语法 (expr for x in iterable),类似列表推导式但返回生成器。适用场景:数据量大、只需遍历一次、作为函数参数(如 sum(x**2 for x in range(n)))。
# 列表推导式(返回列表)
squares_list = [x**2 for x in range(5)] # [0, 1, 4, 9, 16]
# 生成器表达式(返回生成器)
squares_gen = (x**2 for x in range(5)) # <generator object>
print(list(squares_gen)) # [0, 1, 4, 9, 16]
# 内存效率对比
import sys
list_size = sys.getsizeof([x**2 for x in range(1000)])
gen_size = sys.getsizeof((x**2 for x in range(1000)))
print(f"列表大小: {list_size}, 生成器大小: {gen_size}")生成器高级方法#
# yield from - 委托子生成器,可传递 send/throw
def chain(*iterables):
for it in iterables:
yield from it # 等价于 for x in it: yield x
list(chain([1,2], [3,4])) # [1, 2, 3, 4]
# send(value) - 向 yield 表达式发送值,需先 next() 启动
def echo():
while True:
received = yield # yield 作为表达式,可接收 send 的值
print(f"收到: {received}")
g = echo()
next(g) # 启动到第一个 yield
g.send("hi") # 收到: hi
# close() - 在 yield 处注入 GeneratorExit,结束生成器
g = countdown(5)
next(g); next(g)
g.close() # 生成器提前终止
# throw(exc) - 在 yield 处注入指定异常
g = countdown(5)
g.throw(ValueError, "出错") # 在生成器内部抛出异常生成器 vs 列表 选择建议#
| 场景 | 推荐 |
|---|---|
| 数据量大、内存受限 | 生成器 |
| 只需遍历一次 | 生成器 / 生成器表达式 |
| 需多次访问、索引、len() | 列表 |
作为 sum、max 等参数 | 生成器表达式 sum(x for x in ...) |
| 需提前知道全部结果 | 列表 |
推导式#
列表推导式#
# 基本语法:[表达式 for 变量 in 可迭代对象]
squares = [x**2 for x in range(5)] # [0, 1, 4, 9, 16]
# 带条件
evens = [x for x in range(10) if x % 2 == 0] # [0, 2, 4, 6, 8]
# 嵌套循环
matrix = [[i*j for j in range(3)] for i in range(3)]
# [[0, 0, 0], [0, 1, 2], [0, 2, 4]]
# 条件表达式
result = [x if x > 0 else 0 for x in range(-3, 4)]
# [0, 0, 0, 0, 1, 2, 3]字典推导式#
# 基本语法:{键表达式: 值表达式 for 变量 in 可迭代对象}
squares_dict = {x: x**2 for x in range(5)} # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# 带条件
evens_dict = {x: x**2 for x in range(10) if x % 2 == 0}
# 从列表创建字典
names = ["Alice", "Bob", "Charlie"]
name_lengths = {name: len(name) for name in names}
# {'Alice': 5, 'Bob': 3, 'Charlie': 7}集合推导式#
# 基本语法:{表达式 for 变量 in 可迭代对象}
squares_set = {x**2 for x in range(5)} # {0, 1, 4, 9, 16}
# 带条件
evens_set = {x for x in range(10) if x % 2 == 0} # {0, 2, 4, 6, 8}元组推导式?#
Python 没有元组推导式,但可以使用生成器表达式:
# 生成器表达式(类似元组推导式)
squares_gen = (x**2 for x in range(5))
squares_tuple = tuple(squares_gen) # (0, 1, 4, 9, 16)推导式中的海象运算符 (:=)#
Python 3.8+ 支持在推导式中使用 := 赋值并复用:
# 避免重复计算
data = [1, 2, 3, 4, 5]
[y for x in data if (y := x * 2) > 4] # [6, 8, 10]装饰器深入(重点难点)#
带参数的函数的装饰器#
def my_decorator(func):
def wrapper(*args, **kwargs):
print("函数执行前")
result = func(*args, **kwargs)
print("函数执行后")
return result
return wrapper
@my_decorator
def add(a, b):
return a + b
result = add(2, 3)
# 输出:
# 函数执行前
# 函数执行后
# result = 5带参数的装饰器#
def repeat(times):
"""带参数的装饰器"""
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(times=3)
def say_hello():
print("Hello!")
say_hello()
# 输出:
# Hello!
# Hello!
# Hello!多个装饰器#
def bold(func):
def wrapper(*args, **kwargs):
return f"<b>{func(*args, **kwargs)}</b>"
return wrapper
def italic(func):
def wrapper(*args, **kwargs):
return f"<i>{func(*args, **kwargs)}</i>"
return wrapper
@bold
@italic
def hello():
return "Hello"
print(hello()) # <b><i>Hello</i></b>类装饰器#
class CountCalls:
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f"函数被调用了 {self.count} 次")
return self.func(*args, **kwargs)
@CountCalls
def say_hello():
print("Hello!")
say_hello() # 函数被调用了 1 次
say_hello() # 函数被调用了 2 次保留函数元数据的装饰器#
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
"""包装函数"""
return func(*args, **kwargs)
return wrapper
@my_decorator
def say_hello():
"""问候函数"""
print("Hello!")
print(say_hello.__name__) # say_hello(而不是wrapper)
print(say_hello.__doc__) # 问候函数(而不是"包装函数")常用内置装饰器#
from functools import lru_cache, wraps
# lru_cache - 缓存函数结果(备忘)
@lru_cache(maxsize=128)
def fib(n):
return n if n < 2 else fib(n-1) + fib(n-2)
# 单例模式
def singleton(cls):
instances = {}
@wraps(cls)
def get_instance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance解包进阶#
序列解包与 * 运算符#
# 基本解包
a, *rest, b = [1, 2, 3, 4, 5] # a=1, rest=[2,3,4], b=5
# 函数调用解包
def add(a, b, c): return a + b + c
add(*[1, 2, 3]) # 6
add(**{"a":1, "b":2, "c":3}) # 6
# 合并可迭代对象
[*[1,2], *[3,4], 5] # [1, 2, 3, 4, 5]
{**{"a":1}, **{"b":2}} # {"a":1, "b":2}上下文管理器深入#
使用 contextlib#
from contextlib import contextmanager
@contextmanager
def my_context():
print("进入上下文")
try:
yield "资源"
finally:
print("退出上下文")
# 使用
with my_context() as resource:
print(f"使用 {resource}")多个上下文管理器#
from contextlib import ExitStack
# 动态管理多个 with
with open("a.txt") as f1, open("b.txt") as f2:
pass
# ExitStack - 运行时决定上下文数量
filenames = ["a.txt", "b.txt"]
with ExitStack() as stack:
files = [stack.enter_context(open(f)) for f in filenames]上下文管理器协议#
class MyContextManager:
def __enter__(self):
print("进入上下文")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("退出上下文")
# 返回False表示不抑制异常
# 返回True表示抑制异常
return False
# 使用
with MyContextManager() as cm:
print("在上下文中")描述符(Descriptor)#
描述符是实现了 __get__、__set__ 或 __delete__ 方法的对象。
class Descriptor:
def __init__(self, name):
self.name = name
def __get__(self, obj, objtype=None):
print(f"获取 {self.name}")
return obj.__dict__.get(self.name)
def __set__(self, obj, value):
print(f"设置 {self.name} = {value}")
obj.__dict__[self.name] = value
class MyClass:
x = Descriptor("x")
def __init__(self, x):
self.x = x
obj = MyClass(10)
print(obj.x) # 获取 x, 然后输出 10
obj.x = 20 # 设置 x = 20属性访问魔法方法#
class MyClass:
def __init__(self):
self._value = 0
def __getattribute__(self, name):
"""访问任何属性时调用"""
print(f"访问属性: {name}")
return super().__getattribute__(name)
def __getattr__(self, name):
"""访问不存在的属性时调用"""
print(f"属性 {name} 不存在")
return None
def __setattr__(self, name, value):
"""设置属性时调用"""
print(f"设置属性 {name} = {value}")
super().__setattr__(name, value)
def __delattr__(self, name):
"""删除属性时调用"""
print(f"删除属性: {name}")
super().__delattr__(name)元类(Metaclass)#
元类是创建类的类,是 Python 的高级特性。
class MyMeta(type):
def __new__(cls, name, bases, attrs):
# 在创建类之前可以修改类
attrs['created_by'] = 'MyMeta'
return super().__new__(cls, name, bases, attrs)
class MyClass(metaclass=MyMeta):
pass
print(MyClass.created_by) # MyMeta协程和 async/await#
import asyncio
async def fetch_data():
"""异步函数"""
await asyncio.sleep(1)
return "数据"
async def main():
result = await fetch_data()
print(result)
# 运行
asyncio.run(main())并发执行#
# 并发执行多个协程
async def main():
# gather - 等待所有任务完成
results = await asyncio.gather(
fetch_data(), fetch_data(), fetch_data()
)
# create_task - 创建任务,不阻塞
task = asyncio.create_task(fetch_data())
# 可先执行其他操作...
result = await taskfunctools 与 itertools#
functools.partial#
固定部分参数,生成新函数:
from functools import partial
def power(base, exp): return base ** exp
square = partial(power, exp=2)
square(5) # 25itertools 核心工具#
与迭代器配合使用,处理序列的常用工具:
from itertools import chain, islice, groupby, cycle, repeat
# chain - 链式迭代
list(chain([1,2], [3,4])) # [1, 2, 3, 4]
# islice - 切片迭代器(不预加载)
list(islice(range(100), 2, 10, 2)) # [2, 4, 6, 8]
# groupby - 按 key 分组(需先排序)
for k, g in groupby("AAAABBBCC", key=lambda x: x):
print(k, list(g)) # A ['A','A','A','A'] ...
# cycle - 无限循环;repeat - 重复元素最佳实践#
- 优先使用生成器处理大序列,避免一次性加载
- 推导式要简洁,复杂逻辑用显式循环
- 合理使用装饰器,用
@wraps保留元数据,避免过度装饰 - 理解上下文管理器,资源获取/释放用
with - itertools + 生成器 可高效处理流式数据
- 谨慎使用元类、描述符,保持代码可读性
下一步#
掌握了高级特性后,可以学习:
- 常用内置函数 - 学习常用的内置函数,提高编程效率