☰
Python中的类对象示例详解
2026/10/9 12:31:49 网站建设 项目流程

前言


"类对象"这个说法经常被误用。有人用它泛指"类的实例",有人用它指"类本身"。本文取后者:类本身也是一个对象。这一句不是修辞——你可以把类赋值给变量、放进字典、当参数传、当返回值返回,就像操作任何普通对象一样。


另一个常见误解是"类只能靠class语法创建"。实际上class语句只是调用元类type的语法糖,你也可以直接用type()动态造类。


本文用一系列示例展示"类作为对象"的方方面面:类的元数据、动态创建、类作为一等公民传递、可调用对象,以及子类钩子。


一、类对象的元数据


每个类都带有一组属性,描述它自己叫什么、属于哪个模块、继承自谁。


# 适用于 Python 3.8+
class Animal:
pass


class Dog(Animal):
"""一只狗。"""


print(Dog.__name__) # Dog
print(Dog.__qualname__) # Dog
print(Dog.__module__) # __main__
print(Dog.__doc__) # 一只狗。
print(Dog.__bases__) # (<class '__main__.Animal'>,)
print(type(Dog)) # <class 'type'>
print(isinstance(Dog, type)) # True



属性含义



__name__类名(短名)

__qualname__限定名,含外层路径

__module__定义所在的模块名

__bases__直接基类元组

__mro__方法解析顺序元组

__dict__类的命名空间

type(cls)元类,默认是type



因为type(Dog)是type,所以"类"也是某个类的实例——它的实例所属类型就是元类。


二、用type()动态创建类


type有两个用法:单参数调用返回对象类型,三参数调用创建新类。三参数形式是type(类名, 基类元组, 命名空间字典)。


# 适用于 Python 3.8+
def greet(self):
return f"你好,{self.name}"


def make(self, name):
self.name = name


Person = type("Person", (object,), {
"__init__": make,
"greet": greet,
"species": "人",
})

p = Person("Ada")
print(p.greet()) # 你好,Ada
print(Person.__name__) # Person
print(p.species) # 人

这说明了class语句的本质:它执行类体,收集出一个命名空间字典,然后交给元类构造类对象。理解这点,后面学元类就顺了。


三、类是一等公民


类可以像普通值一样被传递和存储,这让"注册表"和"工厂"模式变得非常自然。


# 适用于 Python 3.8+
class JsonParser:
name = "json"

def parse(self, text):
return f"解析 json: {text}"


class CsvParser:
name = "csv"

def 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.8+
def describe(cls):
return f"{cls.__name__} 继承自 {[b.__name__ for b in cls.__bases__]}"


print(describe(JsonParser)) # JsonParser 继承自 ['object']

四、类也可被调用,实例也可以


调用类会创建实例(由元类的__call__完成);而如果一个类定义了__call__,它的实例也能像函数一样被调用。


# 适用于 Python 3.8+
class Multiplier:
def __init__(self, factor):
self.factor = factor

def __call__(self, x):
return x * self.factor


double = Multiplier(2)
print(double(10)) # 20
print(callable(double)) # True
print(callable(Multiplier)) # True

判断"能不能调用"用内置的callable(),它同时适用于类、函数、定义了__call__的实例。


五、子类钩子__init_subclass__


__init_subclass__(Python 3.6 引入)是定义在父类里的类方法,每当有子类被创建时自动调用,常用来做注册或校验。


# 适用于 Python 3.8+
class Plugin:
registry = {}

def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
Plugin.registry[cls.__name__] = cls


class AuthPlugin(Plugin):
pass


class CachePlugin(Plugin):
pass


print(sorted(Plugin.registry)) # ['AuthPlugin', 'CachePlugin']

注意__init_subclass__是隐式类方法,第一个参数是新建的子类cls,不需要@classmethod装饰器。


六、一个综合示例


# 适用于 Python 3.8+
class Model:
table = None

def __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_id
self.name = name


class Post(Model):
table = "posts"

def __init__(self, title):
self.title = title


print(User.table) # user(由钩子自动补)
print(Post.table) # posts(子类自己指定)
print(User(1, "Ada")) # User(**{'user_id': 1, 'name': 'Ada'})

常见坑点



  1. 把"类对象"和"类的实例"混为一谈


❌


``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__``



  1. type(x) == SomeClass无法匹配子类


❌


``python # 适用于 Python 3.8+ print(type(Dog()) == Animal) # False``


✅


``python # 适用于 Python 3.8+ print(isinstance(Dog(), Animal)) # True``



  1. 用type()动态建类时忘了传基类元组


❌


``python # 适用于 Python 3.8+ type("A", object, {}) # TypeError:第二参必须是元组``


✅


``python # 适用于 Python 3.8+ type("A", (object,), {})``



  1. 在__init_subclass__里写实例参数


❌


``python # 适用于 Python 3.8+ def __init_subclass__(self): # 名字错,参数其实是子类 ...``


✅


``python # 适用于 Python 3.8+ def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs)``



  1. 以为callable(cls)是False


❌


``python # 适用于 Python 3.8+ assert not callable(Animal) # AssertionError,类其实可调用``


✅


``python # 适用于 Python 3.8+ print(callable(Animal)) # True``



  1. 动态造类时字典里写了实例方法却忘了 self


❌


```python # 适用于 Python 3.8+ def greet(): # 缺 self return "hi"


C = type("C", (object,), {"greet": greet}) C().greet() # TypeError ```


✅


```python # 适用于 Python 3.8+ def greet(self): return "hi"


C = type("C", (object,), {"greet": greet}) print(C().greet()) # hi ```



  1. 在子类里访问父类__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语句的底层形式来理解,很多高级用法都会变得顺理成章。




需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询