Python 语言基础与高级特性

Python装饰器、上下文管理器、元类、描述符等高级用法

这部分内容是 Python 高级编程的核心。它们不仅体现语言特性的掌握深度,更关系能否写出可复用、可扩展、可维护的高质量代码。

我们逐个详细解释:

Python 装饰器(Decorator)

是什么?

装饰器是一种语法糖,用于在不修改原函数代码的前提下,增强或修改函数的行为。本质是一个“高阶函数”——接收函数作为参数,并返回一个新函数。

基本结构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("Before function call")
        result = func(*args, **kwargs)
        print("After function call")
        return result
    return wrapper

@my_decorator
def say_hello():
    print("Hello!")

say_hello()
# 输出:
# Before function call
# Hello!
# After function call

带参数的装饰器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
def repeat(n):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(n):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(3)
def greet():
    print("Hi!")

greet()
# 输出三次 "Hi!"

类装饰器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class CountCalls:
    def __init__(self, func):
        self.func = func
        self.count = 0

    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"Called {self.count} times")
        return self.func(*args, **kwargs)

@CountCalls
def say_hi():
    print("Hi")

say_hi()  # Called 1 times
say_hi()  # Called 2 times

常见应用场景

  • 日志记录
  • 权限校验
  • 缓存(如 @functools.lru_cache
  • 性能计时
  • 重试机制
  • 接口限流

相关问题

  • 装饰器如何保持原函数的元信息?→ 使用 @functools.wraps(func)
  • 多个装饰器的执行顺序?→ 从下到上装饰,从上到下执行
  • 如何实现带参数和不带参数通用的装饰器?

上下文管理器(Context Manager)

是什么?

用于管理资源的“进入”和“退出”逻辑,确保资源(如文件、锁、数据库连接)一定被释放,即使发生异常。

最常见语法:with 语句。

实现方式一:类 + __enter__ / __exit__

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class MyFileHandler:
    def __init__(self, filename):
        self.filename = filename
        self.file = None

    def __enter__(self):
        print("Opening file...")
        self.file = open(self.filename, 'w')
        return self.file

    def __exit__(self, exc_type, exc_val, exc_tb):
        print("Closing file...")
        if self.file:
            self.file.close()
        # 返回 True 表示“异常已处理”,不向上抛出
        if exc_type:
            print(f"Exception occurred: {exc_val}")
        return False  # 不压制异常

with MyFileHandler('test.txt') as f:
    f.write("Hello Context Manager!")
    # raise ValueError("Oops!")  # 可测试异常是否被处理

实现方式二:使用 @contextlib.contextmanager 装饰器(推荐用于简单场景)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from contextlib import contextmanager

@contextmanager
def my_timer():
    import time
    start = time.time()
    yield  # 这里相当于 __enter__ 和 __exit__ 的分界
    end = time.time()
    print(f"Execution time: {end - start:.2f}s")

with my_timer():
    sum(i for i in range(1000000))

应用场景

  • 文件操作(自动关闭)
  • 数据库事务(自动 commit/rollback)
  • 锁管理(自动 acquire/release)
  • 计时、日志、临时环境变量修改

相关问题

  • __exit__ 的三个参数是什么?→ exc_type, exc_value, traceback
  • 如何在 __exit__ 中“吞掉”异常?→ return True
  • contextmanager 装饰器内部原理?→ 基于生成器 + try/finally

元类(Metaclass)

是什么?

“类的类”。默认情况下,类是由 type 创建的。元类允许你自定义类的创建过程

类 → 实例
元类 → 类

基本用法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class MyMeta(type):
    def __new__(cls, name, bases, attrs):
        print(f"Creating class {name}")
        # 可以在这里修改类属性、方法等
        attrs['added_by_meta'] = True
        return super().__new__(cls, name, bases, attrs)

class MyClass(metaclass=MyMeta):
    pass

# 输出:Creating class MyClass
print(MyClass.added_by_meta)  # True

更实用的例子:自动注册子类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class PluginMeta(type):
    plugins = {}

    def __new__(cls, name, bases, attrs):
        new_class = super().__new__(cls, name, bases, attrs)
        if name != 'BasePlugin':  # 不注册基类
            cls.plugins[name] = new_class
        return new_class

class BasePlugin(metaclass=PluginMeta):
    pass

class EmailPlugin(BasePlugin):
    def send(self): pass

class SMSPlugin(BasePlugin):
    def send(self): pass

print(PluginMeta.plugins)
# {'EmailPlugin': <class '__main__.EmailPlugin'>, 'SMSPlugin': <class '__main__.SMSPlugin'>}

应用场景

  • ORM 框架(如 Django Model、SQLAlchemy)自动建表
  • 插件系统自动注册
  • 接口强制规范(如要求所有子类实现某个方法)
  • 单例模式、抽象基类控制

相关问题

  • __new____init__ 在元类中的区别?
    • __new__ 创建类对象,__init__ 初始化类对象
  • 为什么说“元类是深水区”?→ 滥用会导致代码难以理解和调试
  • Django Model 是如何使用元类的?→ ModelBase 收集字段、建立映射、注册到 apps

描述符(Descriptor)

是什么?

描述符是实现了特定协议方法__get__, __set__, __delete__)的对象,用于自定义属性访问行为

它是 Python 面向对象中“属性管理”的底层机制,@propertyclassmethodstaticmethod 都是基于描述符实现的。

基本结构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class NonNegative:
    def __init__(self, name):
        self.name = name

    def __get__(self, instance, owner):
        if instance is None:
            return self
        return instance.__dict__.get(self.name, 0)

    def __set__(self, instance, value):
        if value < 0:
            raise ValueError("Value cannot be negative")
        instance.__dict__[self.name] = value

class Product:
    price = NonNegative('price')
    quantity = NonNegative('quantity')

    def __init__(self, name, price, quantity):
        self.name = name
        self.price = price
        self.quantity = quantity

p = Product("Apple", 5, 10)
print(p.price)  # 5
p.price = -1    # ValueError: Value cannot be negative

数据描述符 vs 非数据描述符

  • 数据描述符:实现了 __set____delete__ → 优先级高于实例字典
  • 非数据描述符:只实现 __get__ → 实例字典优先

应用场景

  • 数据校验(如非负、类型检查、范围限制)
  • 延迟计算属性(懒加载)
  • ORM 字段定义(如 Django Field)
  • 缓存属性值(避免重复计算)

相关问题

  • 描述符和 @property 的关系?→ @property 是描述符的语法糖
  • 为什么 instance.__dict__[name] = value 而不是 setattr(instance, name, value)?→ 避免无限递归调用 __set__
  • 描述符是类属性,为什么能管理不同实例的值?→ 通过 instance 参数区分不同对象

总结对比表

特性用途触发时机典型应用场景
装饰器增强函数/类行为函数定义/调用时日志、缓存、权限
上下文管理器管理资源生命周期with 语句进入/退出时文件、锁、事务
元类控制类的创建过程类定义时ORM、插件注册、强制规范
描述符自定义属性访问行为属性读/写/删除时数据校验、延迟计算、ORM

掌握这四个高级特性,不仅能让你写出更优雅、健壮的代码,更能体现你对 Python 语言设计哲学的理解 —— “简洁但不简单,灵活但有章法”。

它们是区分“普通开发者”和“资深架构师”的重要分水岭。

版权声明:本文为原创,依据 CC BY-NC-SA 4.0 许可证进行授权,转载请附上出处链接及本声明。