
元类和描述符是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, objtypeNone): 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, objtypeNone): 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五、getattrvsgetattributepython复制下载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(metaclassMeta): 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(metaclassSingletonMeta): 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(metaclassPluginMeta): 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()九、实战迷你ORMpython复制下载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, objtypeNone): 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_length255): super().__init__(fVARCHAR({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(metaclassModelMeta): def save(self): cols , .join(self._fields.keys()) placeholders , .join([%s] * len(self._fields)) sql fINSERT 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的底层。日常开发不建议滥用元类但理解它们能让你读懂框架源码也能在合适场景写出优雅的代码。