python-元类的学习

16 阅读3分钟

1. 背景

在Python中,元类(metaclass)是一种高级概念 ,用于控制类的创建过程。简单来说,元类就是“类的类”,它负责生成我们日常使用的类对象。当我们定义一个类时 ,Python解释器实际上会调用元类来创建这个类。默认情况下,所有类都使用type作为其元类,但开发者可以通过自定义元类来修改类的行为或自动添加额外的功能。

2. 使用

2.1 使用type作为元类

def my_class_method(self):
    print("Hello from my_class_method")

MyClass = type('MyClass', (object,), {'my_method': my_class_method})

instance = MyClass()
instance.my_method()

2.2 自定义元类实现日志记录功能

import logging  
  
class LoggerMeta(type):  
    def __new__(cls, name, bases, attrs):  
        new_class = super().__new__(cls, name, bases, attrs)  
        # 添加日志记录功能  
        if not hasattr(new_class, 'log'):  
            new_class.log = logging.getLogger(name)  
        return new_class  
  
class MyLoggedClass(metaclass=LoggerMeta):  
    def __init__(self, message):  
        self.log.info(f'Creating instance with message: {message}')  
  
# 设置日志级别为INFO  
logging.basicConfig(level=logging.INFO)  
  
# 创建实例 ,日志将自动记录  
instance = MyLoggedClass('Hello, logging!')

2.3 类属性检查实例

class AttributeCheckerMeta(type):
    def __new__(cls, name, bases, attrs):
        if 'check_attribute' not in attrs:
            raise TypeError(f"{name} class must define 'check_attribute'")
        return super().__new__(cls, name, bases, attrs)

class MyClass(metaclass=AttributeCheckerMeta):
    check_attribute = True  # 这个属性是必需的
    another_attribute = False

# 尝试创建未定义check_attribute的类将抛出TypeError
try:
    class MissingAttributeClass(metaclass=AttributeCheckerMeta):
        pass
except TypeError as e:
    print(e)

2.4 动态修改类属性和方法

class VersionMeta(type):  
    def __new__(cls, name, bases, dct):  
        dct['version'] = '1.0.0'  # 动态添加版本号属性  
        for attr_name, attr_value in dct.items():  
            if callable(attr_value):  # 如果是方法,则添加性能监控装饰器  
                dct[attr_name] = cls.performance_monitor(attr_value)  
        return super().__new__(cls, name, bases, dct)  
      
    @staticmethod  
    def performance_monitor(func):  
        import time  
          
        def wrapper(*args, **kwargs):  
            start_time = time.time()  
            result = func(*args, **kwargs)  
            end_time = time.time()  
            print(f"{func.__name__} took {end_time - start_time:.4f} seconds")  
            return result  
        return wrapper  
  
class MyClass(metaclass=VersionMeta):  
    def heavy_computation(self):  
        sum([i for i in range(10**6)])  
  
instance = MyClass()  
print(instance.version)  # 显示版本号  
instance.heavy_computation()  # 自动监控执行时间

2.5 创建单例模式

class SingletonMeta(type):  
    _instances = {}  
      
    def __call__(cls, *args, **kwargs):  
        if cls not in cls._instances:  
            cls._instances[cls] = super().__call__(*args, **kwargs)  
        return cls._instances[cls]  
  
class Singleton(metaclass=SingletonMeta):  
    pass  
  
singleton1 = Singleton()  
singleton2 = Singleton()  
  
print(singleton1 is singleton2)  # 输出True,证明是同一个实例

2.6 自动注册类到全局列表

class AutoRegisterMeta(type):  
    registry = []  
      
    def __new__(cls, name, bases, dct):  
        new_class = super().__new__(cls, name, bases, dct)  
        cls.registry.append(new_class)  
        return new_class  
  
class Registerable(metaclass=AutoRegisterMeta):  
    pass  
  
class MyClass(Registerable):  
    pass  
  
class AnotherClass(Registerable):  
    pass  
  
print(AutoRegisterMeta.registry)  # 显示已注册的类列表

2.7 数据库表映射到类

class ORMBaseMeta(type):  
    def __new__(cls, name, bases, dct):  
        fields = {}  
        for key, value in dct.items():  
            if isinstance(value, str):  
                fields[key] = value  
        dct['_fields'] = fields  
        return super().__new__(cls, name, bases, dct)  
  
class User(metaclass=ORMBaseMeta):  
    id = 'INT PRIMARY KEY'  
    name = 'VARCHAR(100)'  
    email = 'VARCHAR(100)'  
  
print(User._fields)  # 输出字段信息  
  
# 基于字段信息生成SQL创建表语句  
create_table_sql = f"CREATE TABLE IF NOT EXISTS {User.__name__} (" + ", ".join([f"{k} {v}" for k, v in User._fields.items()]) + ");"  
print(create_table_sql)

2.8 元类装饰器应用

class TimingMeta(type):  
    def __new__(cls, name, bases, dct):  
        for key, value in dct.items():  
            if callable(value) and not key.startswith("__"):  # 排除特殊方法  
                dct[key] = cls.timer_decorator(value)  
        return super().__new__(cls, name, bases, dct)  
      
    @staticmethod  
    def timer_decorator(func):  
        import time  
          
        def wrapper(*args, **kwargs):  
            start_time = time.time()  
            result = func(*args, **kwargs)  
            end_time = time.time()  
            print(f"{func.__name__} executed in {end_time - start_time:.6f}s")  
            return result  
        return wrapper  
  
class TimedClass(metaclass=TimingMeta):  
    def heavy_computation(self):  
        sum(range(10**6))  
  
tc = TimedClass()  
tc.heavy_computation()