Python中的类对象示例详解

📅 发布时间:2026/10/9 12:32:03
Python中的类对象示例详解
前言类对象这个说法经常被误用。有人用它泛指类的实例有人用它指类本身。本文取后者类本身也是一个对象。这一句不是修辞——你可以把类赋值给变量、放进字典、当参数传、当返回值返回就像操作任何普通对象一样。另一个常见误解是类只能靠class语法创建。实际上class语句只是调用元类type的语法糖你也可以直接用type()动态造类。本文用一系列示例展示类作为对象的方方面面类的元数据、动态创建、类作为一等公民传递、可调用对象以及子类钩子。一、类对象的元数据每个类都带有一组属性描述它自己叫什么、属于哪个模块、继承自谁。# 适用于 Python 3.8class Animal:passclass Dog(Animal):一只狗。print(Dog.__name__) # Dogprint(Dog.__qualname__) # Dogprint(Dog.__module__) # __main__print(Dog.__doc__) # 一只狗。print(Dog.__bases__) # (class __main__.Animal,)print(type(Dog)) # class typeprint(isinstance(Dog, type)) # True属性含义__name__类名短名__qualname__限定名含外层路径__module__定义所在的模块名__bases__直接基类元组__mro__方法解析顺序元组__dict__类的命名空间type(cls)元类默认是type因为type(Dog)是type所以类也是某个类的实例——它的实例所属类型就是元类。二、用type()动态创建类type有两个用法单参数调用返回对象类型三参数调用创建新类。三参数形式是type(类名, 基类元组, 命名空间字典)。# 适用于 Python 3.8def greet(self):return f你好{self.name}def make(self, name):self.name namePerson type(Person, (object,), {__init__: make,greet: greet,species: 人,})p Person(Ada)print(p.greet()) # 你好Adaprint(Person.__name__) # Personprint(p.species) # 人这说明了class语句的本质它执行类体收集出一个命名空间字典然后交给元类构造类对象。理解这点后面学元类就顺了。三、类是一等公民类可以像普通值一样被传递和存储这让注册表和工厂模式变得非常自然。# 适用于 Python 3.8class JsonParser:name jsondef parse(self, text):return f解析 json: {text}class CsvParser:name csvdef parse(self, text):return f解析 csv: {text}registry {cls.name: cls for cls in (JsonParser, CsvParser)}def build(fmt):cls registry.get(fmt)if cls is None:raise ValueError(f不支持的格式: {fmt})return cls() # 类对象可直接调用print(build(csv).parse(a,b)) # 解析 csv: a,b这里registry里的值就是类对象本身cls()通过调用类来创建实例。# 适用于 Python 3.8def describe(cls):return f{cls.__name__} 继承自 {[b.__name__ for b in cls.__bases__]}print(describe(JsonParser)) # JsonParser 继承自 [object]四、类也可被调用实例也可以调用类会创建实例由元类的__call__完成而如果一个类定义了__call__它的实例也能像函数一样被调用。# 适用于 Python 3.8class Multiplier:def __init__(self, factor):self.factor factordef __call__(self, x):return x * self.factordouble Multiplier(2)print(double(10)) # 20print(callable(double)) # Trueprint(callable(Multiplier)) # True判断能不能调用用内置的callable()它同时适用于类、函数、定义了__call__的实例。五、子类钩子__init_subclass____init_subclass__Python 3.6 引入是定义在父类里的类方法每当有子类被创建时自动调用常用来做注册或校验。# 适用于 Python 3.8class Plugin:registry {}def __init_subclass__(cls, **kwargs):super().__init_subclass__(**kwargs)Plugin.registry[cls.__name__] clsclass AuthPlugin(Plugin):passclass CachePlugin(Plugin):passprint(sorted(Plugin.registry)) # [AuthPlugin, CachePlugin]注意__init_subclass__是隐式类方法第一个参数是新建的子类cls不需要classmethod装饰器。六、一个综合示例# 适用于 Python 3.8class Model:table Nonedef __init_subclass__(cls, **kwargs):super().__init_subclass__(**kwargs)if cls.table is None:cls.table cls.__name__.lower()def __repr__(self):return f{type(self).__name__}(**{vars(self)!r})class User(Model):def __init__(self, user_id, name):self.user_id user_idself.name nameclass Post(Model):table postsdef __init__(self, title):self.title titleprint(User.table) # user由钩子自动补print(Post.table) # posts子类自己指定print(User(1, Ada)) # User(**{user_id: 1, name: Ada})常见坑点把类对象和类的实例混为一谈❌python # 适用于 Python 3.8 def use(obj): print(obj.__name__) # 传实例进来会 AttributeError✅python # 适用于 Python 3.8 def use(obj): if isinstance(obj, type): return obj.__name__ return type(obj).__name__type(x) SomeClass无法匹配子类❌python # 适用于 Python 3.8 print(type(Dog()) Animal) # False✅python # 适用于 Python 3.8 print(isinstance(Dog(), Animal)) # True用type()动态建类时忘了传基类元组❌python # 适用于 Python 3.8 type(A, object, {}) # TypeError第二参必须是元组✅python # 适用于 Python 3.8 type(A, (object,), {})在__init_subclass__里写实例参数❌python # 适用于 Python 3.8 def __init_subclass__(self): # 名字错参数其实是子类 ...✅python # 适用于 Python 3.8 def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs)以为callable(cls)是False❌python # 适用于 Python 3.8 assert not callable(Animal) # AssertionError类其实可调用✅python # 适用于 Python 3.8 print(callable(Animal)) # True动态造类时字典里写了实例方法却忘了 self❌python # 适用于 Python 3.8 def greet(): # 缺 self return hiC type(C, (object,), {greet: greet}) C().greet() # TypeError ✅python # 适用于 Python 3.8 def greet(self): return hiC type(C, (object,), {greet: greet}) print(C().greet()) # hi 在子类里访问父类__init_subclass__未处理的参数❌python # 适用于 Python 3.8 class Base: def __init_subclass__(cls): pass # 不转发 kwargs带类参数的子类定义会失败✅python # 适用于 Python 3.8 class Base: def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs)总结观点说明类是对象实例类型是元类默认type类可动态创建type(name, bases, ns)类是一等公民可赋值、可传参、可入容器类可调用Cls()创建实例实例可调用定义了__call__即可子类钩子__init_subclass__在子类创建时触发类也是一个对象不是抽象口号它直接解释了工厂模式、注册表和元类为什么能这么写。把类当普通值来用把type三参数形式当class语句的底层形式来理解很多高级用法都会变得顺理成章。