元类和描述符是Python最"魔法"的特性,也是框架的底层基石。ORM的字段、属性的getter/setter、单例模式都依赖它们。本文从描述符讲起,深入元类,并实战一个迷你ORM。
一、属性访问的本质
访问obj.attr时,Python调用type(obj).__getattribute__。若找到数据描述符,优先使用。
python
复制
下载
class User: def __init__(self): self.name = "Tom" u = User() print(u.__dict__) # {'name': 'Tom'} print(User.__dict__.keys()) # 类属性二、描述符协议
实现以下任一方法即为描述符:
__get__(self, obj, objtype)__set__(self, obj, value)__delete__(self, obj)数据描述符:实现
__get__和__set__非数据描述符:只实现
__get__
python
复制
下载
class Descriptor: def __get__(self, obj, objtype=None): print("get") return 42 class Foo: x = Descriptor() f = Foo() print(f.x) # get \n 42三、实战:类型校验描述符
python
复制
下载
class Typed: def __init__(self, name, expected_type): self.name = name self.expected_type = expected_type def __set_name__(self, owner, name): self.name = name def __get__(self, obj, objtype=None): if obj is None: return self return obj.__dict__.get(self.name) def __set__(self, obj, value): if not isinstance(value, self.expected_type): raise TypeError(f"{self.name} 必须是 {self.expected_type.__name__}") obj.__dict__[self.name] = value class User: name = Typed('name', str) age = Typed('age', int) def __init__(self, name, age): self.name = name self.age = age u = User("Tom", 20) # u.age = "abc" # TypeError四、property:内置描述符
python
复制
下载
class Temperature: def __init__(self, celsius): self._celsius = celsius @property def celsius(self): return self._celsius @celsius.setter def celsius(self, value): if value < -273.15: raise ValueError("低于绝对零度") self._celsius = value @property def fahrenheit(self): return self._celsius * 9 / 5 + 32 t = Temperature(25) print(t.fahrenheit) # 77.0五、getattrvsgetattribute
python
复制
下载
class Foo: def __getattr__(self, name): # 只在属性找不到时调用 return f"默认值: {name}" def __getattribute__(self, name): # 所有属性访问都调用(慎用) print(f"访问 {name}") return super().__getattribute__(name)六、元类基础
元类是"类的类"。type是所有类的元类。
python
复制
下载
# 动态创建类 User = type('User', (object,), {'name': 'Tom'}) u = User() print(u.name) # Tom # 自定义元类 class Meta(type): def __new__(mcs, name, bases, namespace): print(f"创建类 {name}") namespace['created'] = True return super().__new__(mcs, name, bases, namespace) class User(metaclass=Meta): pass print(User.created) # True七、元类实战:单例
python
复制
下载
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 Config(metaclass=SingletonMeta): def __init__(self): self.debug = False c1 = Config() c2 = Config() print(c1 is c2) # True八、元类实战:注册表
python
复制
下载
class PluginMeta(type): registry = {} def __new__(mcs, name, bases, namespace): cls = super().__new__(mcs, name, bases, namespace) if name != 'Plugin': mcs.registry[namespace.get('name', name)] = cls return cls class Plugin(metaclass=PluginMeta): name = '' def run(self): raise NotImplementedError class EmailPlugin(Plugin): name = 'email' def run(self): print("发送邮件") class SMSPlugin(Plugin): name = 'sms' def run(self): print("发送短信") print(PluginMeta.registry) # {'email': EmailPlugin, 'sms': SMSPlugin} plugin = PluginMeta.registry['email']() plugin.run()九、实战:迷你ORM
python
复制
下载
class Field: def __init__(self, column_type): self.column_type = column_type def __set_name__(self, owner, name): self.name = name def __get__(self, obj, objtype=None): if obj is None: return self return obj.__dict__.get(self.name) def __set__(self, obj, value): obj.__dict__[self.name] = value class IntegerField(Field): def __init__(self): super().__init__('INTEGER') class StringField(Field): def __init__(self, max_length=255): super().__init__(f'VARCHAR({max_length})') class ModelMeta(type): def __new__(mcs, name, bases, namespace): if name == 'Model': return super().__new__(mcs, name, bases, namespace) fields = {} for k, v in namespace.items(): if isinstance(v, Field): fields[k] = v cls = super().__new__(mcs, name, bases, namespace) cls._table = name.lower() cls._fields = fields return cls def __init__(cls, name, bases, namespace): super().__init__(name, bases, namespace) if name != 'Model': print(f"表 {cls._table} 创建完成") class Model(metaclass=ModelMeta): def save(self): cols = ', '.join(self._fields.keys()) placeholders = ', '.join(['%s'] * len(self._fields)) sql = f"INSERT INTO {self._table} ({cols}) VALUES ({placeholders})" values = [getattr(self, k) for k in self._fields] print(sql, values) class User(Model): name = StringField(50) age = IntegerField() u = User() u.name = "Tom" u.age = 20 u.save() # INSERT INTO user (name, age) VALUES (%s, %s) ['Tom', 20]十、总结
描述符控制属性访问,元类控制类创建。它们是ORM、框架、DSL的底层。日常开发不建议滥用元类,但理解它们能让你读懂框架源码,也能在合适场景写出优雅的代码。