
1. 引言Python 是一门「一切皆对象」的语言。无论是整数、字符串、函数还是类本身在 Python 中都是对象。理解 Python 的对象模型Object Model与数据模型Data Model是深入掌握这门语言的关键分水岭。很多开发者在使用 Python 时停留在「调用 API」的层面而真正的高手则能理解「API 背后的机制」。本文将从三个核心主题展开魔术方法Magic Methods如何通过__xxx__方法让自定义对象融入 Python 语法。描述符Descriptors属性访问的底层协议property、classmethod、staticmethod的实现基石。元类Metaclasses类的「类」掌控类创建过程的终极武器。读完本文你将理解 Python 对象系统的底层运作逻辑并能写出更优雅、更 Pythonic 的代码。2. 对象模型一切皆对象2.1 对象的基本概念在 Python 中对象是数据与行为的封装体。每个对象都有三个核心属性身份Identity对象在内存中的唯一标识可通过id()获取。类型Type对象所属的类可通过type()获取。值Value对象承载的数据内容。a42id(a)140736518948720type(a)classint2.2 类也是对象这是 Python 对象模型中最反直觉、也最重要的一点类本身也是对象。当你定义一个类时Python 会创建一个类型为type的对象classDog:defbark(self):returnWoof!print(type(Dog))# class typeDog是type的实例这意味着类可以被赋值给变量、作为参数传递、动态创建——这正是元类机制的基石。2.3 对象的创建与销毁对象的生命周期由__new__、__init__、__del__三个魔术方法协作管理classPerson:def__new__(cls,name):print(1. __new__ 分配内存)instancesuper().__new__(cls)returninstancedef__init__(self,name):print(2. __init__ 初始化)self.namenamedef__del__(self):print(3. __del__ 销毁前清理)pPerson(Alice)# 输出# 1. __new__ 分配内存# 2. __init__ 初始化delp# 输出# 3. __del__ 销毁前清理3. 数据模型魔术方法3.1 什么是魔术方法魔术方法Magic Methods又称双下划线方法Dunder Methods是 Python 数据模型的核心。它们以双下划线开头和结尾如__len__、__getitem__不需要显式调用而是在特定语法场景下由 Python 解释器自动触发。3.2 字符串表示__str__与__repr__classBook:def__init__(self,title,author):self.titletitle self.authorauthordef__repr__(self):面向开发者的表示尽量无歧义returnfBook({self.title}, {self.author})def__str__(self):面向用户的友好表示returnf《{self.title}》 by{self.author}bookBook(三体,刘慈欣)print(repr(book))# Book(三体, 刘慈欣)print(str(book))# 《三体》 by 刘慈欣print(book)# 《三体》 by 刘慈欣print 优先调用 __str__3.3 容器协议__len__与__getitem__实现这两个方法自定义类就能像列表一样支持len()和下标访问classPlaylist:def__init__(self,songs):self._songssongsdef__len__(self):returnlen(self._songs)def__getitem__(self,index):returnself._songs[index]def__setitem__(self,index,song):self._songs[index]songdef__iter__(self):returniter(self._songs)plPlaylist([晴天,七里香,夜曲])print(len(pl))# 3print(pl[1])# 七里香pl[2]稻香forsonginpl:# 有 __iter__ 后可迭代print(song)3.4 运算符重载通过魔术方法可以让自定义对象支持、、等运算符classVector:def__init__(self,x,y):self.xx self.yydef__add__(self,other):实现 v1 v2returnVector(self.xother.x,self.yother.y)def__eq__(self,other):实现 v1 v2returnself.xother.xandself.yother.ydef__lt__(self,other):实现 v1 v2按模长比较return(self.x**2self.y**2)(other.x**2other.y**2)def__repr__(self):returnfVector({self.x},{self.y})v1Vector(1,2)v2Vector(3,4)print(v1v2)# Vector(4, 6)print(v1Vector(1,2))# Trueprint(v1v2)# True3.5 可调用对象__call__给类加上__call__方法后实例可以像函数一样被调用classCounter:def__init__(self):self.count0def__call__(self):self.count1returnself.count counterCounter()print(counter())# 1print(counter())# 2print(counter())# 33.6 上下文管理器__enter__与__exit__实现这两个方法自定义类就能配合with语句使用classTimer:def__enter__(self):importtime self.starttime.time()returnselfdef__exit__(self,exc_type,exc_val,exc_tb):importtime elapsedtime.time()-self.startprint(f耗时{elapsed:.4f}秒)returnFalse# False 表示不吞掉异常withTimer():totalsum(range(1_000_000))3.7 常用魔术方法速查表类别方法触发场景创建与销毁__new__/__init__/__del__实例化、销毁字符串表示__str__/__repr__/__format__print()、repr()、format()容器协议__len__/__getitem__/__setitem__/__delitem__/__contains__/__iter__len()、下标访问、in、迭代属性访问__getattr__/__setattr__/__getattribute__/__delattr__属性读写删除运算符__add__/__sub__/__mul__/__eq__/__lt__等算术、比较运算调用__call__实例像函数一样调用上下文管理__enter__/__exit__with语句哈希__hash__hash()、作为字典键4. 描述符属性访问的底层协议4.1 什么是描述符描述符Descriptor是实现了__get__、__set__、__delete__中至少一个方法的对象。它用于拦截属性访问是 Python 属性机制的底层协议。classDescriptor:def__get__(self,instance,owner):print(访问属性)returndescriptor valuedef__set__(self,instance,value):print(f设置属性{value})classMyClass:attrDescriptor()objMyClass()print(obj.attr)# 访问属性 → descriptor valueobj.attr100# 设置属性1004.2 描述符的分类根据实现的方法描述符分为两类数据描述符Data Descriptor同时实现__get__和__set__优先级最高。非数据描述符Non-data Descriptor只实现__get__优先级低于实例字典。classDataDesc:数据描述符同时有 __get__ 和 __set__def__get__(self,instance,owner):returndata descriptordef__set__(self,instance,value):print(fset{value})classNonDataDesc:非数据描述符只有 __get__def__get__(self,instance,owner):returnnon-data descriptorclassDemo:dataDataDesc()non_dataNonDataDesc()dDemo()d.data1# 触发 __set__不会写入实例字典d.non_data1# 非数据描述符不拦截 __set__写入实例字典print(d.__dict__)# {non_data: 1}print(d.non_data)# 实例字典优先 → 1非数据描述符被遮蔽4.3 属性查找顺序理解属性访问的优先级至关重要classDemo:def__init__(self):self.x100propertydefx(self):returnself._xx.setterdefx(self,value):self._xvalue属性查找顺序为数据描述符类属性中的__get____set__实例字典__dict__非数据描述符只有__get__类属性非描述符__getattr__兜底4.4property的实现原理property本质上就是一个数据描述符classMyProperty:def__init__(self,fgetNone,fsetNone):self.fgetfget self.fsetfsetdef__get__(self,instance,owner):ifinstanceisNone:returnselfreturnself.fget(instance)def__set__(self,instance,value):ifself.fsetisNone:raiseAttributeError(cant set attribute)self.fset(instance,value)defsetter(self,fset):self.fsetfsetreturnselfclassTemperature:def__init__(self,celsius):self._celsiuscelsiusMyPropertydefcelsius(self):returnself._celsiuscelsius.setterdefcelsius(self,value):self._celsiusvalue tTemperature(25)print(t.celsius)# 25t.celsius30print(t.celsius)# 304.5 描述符实战类型校验描述符最常见的应用场景是属性类型校验classTyped:def__init__(self,name,expected_type):self.namename self.expected_typeexpected_typedef__get__(self,instance,owner):ifinstanceisNone:returnselfreturninstance.__dict__[self.name]def__set__(self,instance,value):ifnotisinstance(value,self.expected_type):raiseTypeError(f{self.name}必须是{self.expected_type.__name__}收到{type(value).__name__})instance.__dict__[self.name]valueclassPerson:nameTyped(name,str)ageTyped(age,int)def__init__(self,name,age):self.namename self.ageage pPerson(Alice,30)print(p.name,p.age)# Alice 30# p.age 三十 # TypeError: age 必须是 int收到 str5. 元类类的「类」5.1 什么是元类元类Metaclass是创建类的类。默认情况下所有类都是由type创建的classFoo:passprint(type(Foo))# class type元类的作用是在类创建时拦截并修改类的定义常用于自动注册类校验类定义自动添加方法或属性实现 ORM 中的模型映射5.2 自定义元类通过继承type并重写__new__或__init__来创建元类classMeta(type):def__new__(mcs,name,bases,namespace):print(f创建类{name})# 自动为所有类添加一个属性namespace[created_by]Metareturnsuper().__new__(mcs,name,bases,namespace)classMyClass(metaclassMeta):passprint(MyClass.created_by)# Meta5.3 元类实战自动注册元类最常见的应用是自动注册子类classPluginRegistry(type):registry{}def__new__(mcs,name,bases,namespace):clssuper().__new__(mcs,name,bases,namespace)ifname!Plugin:mcs.registry[name]clsreturnclsclassPlugin(metaclassPluginRegistry):所有插件的基类classAudioPlugin(Plugin):passclassVideoPlugin(Plugin):passprint(PluginRegistry.registry)# {AudioPlugin: class __main__.AudioPlugin, VideoPlugin: class __main__.VideoPlugin}5.4 元类实战单例模式利用元类实现单例比装饰器方式更干净classSingletonMeta(type):_instances{}def__call__(cls,*args,**kwargs):ifclsnotincls._instances:cls._instances[cls]super().__call__(*args,**kwargs)returncls._instances[cls]classDatabase(metaclassSingletonMeta):def__init__(self):print(初始化数据库连接)db1Database()# 初始化数据库连接db2Database()# 不再打印直接返回已有实例print(db1isdb2)# True5.5 元类实战ORM 字段映射元类在 ORM 框架中应用广泛这里模拟一个简化版classField:def__init__(self,column_type):self.column_typecolumn_typeclassModelMeta(type):def__new__(mcs,name,bases,namespace):ifnameModel:returnsuper().__new__(mcs,name,bases,namespace)# 收集字段定义fields{}forkey,valueinnamespace.items():ifisinstance(value,Field):fields[key]value# 从类命名空间中移除字段避免被当作类属性forkeyinfields:delnamespace[key]namespace[_fields]fields namespace[_table_name]name.lower()returnsuper().__new__(mcs,name,bases,namespace)classModel(metaclassModelMeta):def__init__(self,**kwargs):forkey,valueinkwargs.items():setattr(self,key,value)defsave(self):fields, .join(self._fields.keys())print(fINSERT INTO{self._table_name}({fields}) VALUES ...)classUser(Model):idField(INT)nameField(VARCHAR(100))userUser(id1,nameAlice)user.save()# INSERT INTO user (id, name) VALUES ...5.6 元类与__init_subclass__Python 3.6 提供了__init_subclass__在多数场景下可以替代元类更简洁classPlugin:registry{}def__init_subclass__(cls,**kwargs):super().__init_subclass__(**kwargs)Plugin.registry[cls.__name__]clsclassAudioPlugin(Plugin):passprint(Plugin.registry)# {AudioPlugin: class __main__.AudioPlugin}6. 三者的协同一个综合示例下面用一个「带校验的 ORM 模型」综合展示魔术方法、描述符与元类的协作# 1. 描述符字段类型校验classField:def__init__(self,name,column_type):self.namename self.column_typecolumn_typedef__get__(self,instance,owner):ifinstanceisNone:returnself