高级特性#

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 之后继续执行
  • 函数结束时抛出 StopIterationfor 自动处理)

生成器函数#

# 基本生成器函数
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()列表
作为 summax 等参数生成器表达式 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 task

functools 与 itertools#

functools.partial#

固定部分参数,生成新函数:

from functools import partial

def power(base, exp): return base ** exp
square = partial(power, exp=2)
square(5)  # 25

itertools 核心工具#

与迭代器配合使用,处理序列的常用工具:

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 - 重复元素

最佳实践#

  1. 优先使用生成器处理大序列,避免一次性加载
  2. 推导式要简洁,复杂逻辑用显式循环
  3. 合理使用装饰器,用 @wraps 保留元数据,避免过度装饰
  4. 理解上下文管理器,资源获取/释放用 with
  5. itertools + 生成器 可高效处理流式数据
  6. 谨慎使用元类、描述符,保持代码可读性

下一步#

掌握了高级特性后,可以学习:

  1. 常用内置函数 - 学习常用的内置函数,提高编程效率