创建时间: 2026-08-25最后更新: 2026-08-25

1. 认识

类(class)是创建对象的一种模板。它把一组相关的数据和处理这些数据的函数放在一起,形成一个更完整的模型。

例如,一本书有标题、作者这些数据,也可以有介绍自己、修改标题这些行为。用类描述后,每一本具体的书都是一个对象:

book.py
01
class Book:
02
def __init__(self, title, author):
03
self.title = title
04
self.author = author
05
06
def describe(self):
07
return f"《{self.title}》的作者是{self.author}。"
08
09
first_book = Book("Python 入门", "小林")
10
second_book = Book("FastAPI 实战", "小周")
11
12
print(first_book.describe())
13
print(second_book.describe())

Book 是类,first_book 和 second_book 是两个实例。它们使用同一套结构,但各自保存不同的标题和作者。

类适合描述「有状态、有行为」的对象。如果只是临时组合几个字段,字典往往更直接;当数据和操作逐渐变多时,类可以让代码的职责更清晰。

2. 定义

使用 class 定义类,类名通常采用大驼峰命名,例如 Book、ChatSession。类体通过缩进表示:

define.py
1
class User:
2
pass
3
4
user = User()
5
print(user)

pass 表示暂时没有实现任何内容。User() 会创建一个 User 实例,调用类的过程也叫实例化。

通常会在类中定义 __init__(),为实例准备初始数据:

init.py
1
class User:
2
def __init__(self, name, email):
3
self.name = name
4
self.email = email
5
6
user = User("小明", "xiaoming@example.com")
7
print(user.name) # 小明
8
print(user.email) # xiaoming@example.com

__init__() 会在实例创建后自动调用。self 代表当前实例,self.name 和 self.email 是这个实例自己的属性。调用 User("小明", ...) 时,传入的参数会交给 __init__(),不需要手动传 self。

很多教程会把 __init__() 口语化地称为「构造函数」,但从 Python 的严格机制看,它是初始化方法。真正负责创建实例的是 __new__()。调用 User(...) 时,可以把过程简化理解为:先由 __new__() 创建并返回实例,再由 __init__() 给这个实例填充初始数据。

creation-order.py
01
class User:
02
def __new__(cls, name):
03
print("1. __new__: 创建实例")
04
instance = super().__new__(cls)
05
return instance
06
07
def __init__(self, name):
08
print("2. __init__: 初始化实例")
09
self.name = name
10
11
user = User("小明")
12
print(user.name)

输出顺序是先执行 __new__(),再执行 __init__()。__new__() 接收类本身 cls,必须返回一个实例;__init__() 接收已经创建好的实例 self,不需要返回实例。

方法调用时机第一个参数主要职责日常使用
__new__()实例创建之前cls创建并返回实例很少重写
__init__()实例创建之后self初始化实例属性经常使用

如果 __new__() 没有返回当前类的实例,Python 不会继续调用对应的 __init__()。重写 __new__() 常见于继承 str、tuple 等不可变类型,或实现单例等特殊创建逻辑;普通业务类通常只需要编写 __init__()。

每个实例可以拥有不同的属性值,也可以在创建后增加属性:

instance-attributes.py
01
class Account:
02
def __init__(self, owner):
03
self.owner = owner
04
self.balance = 0
05
06
account = Account("小明")
07
account.balance = 100
08
account.currency = "CNY"
09
10
print(account.owner)
11
print(account.balance)
12
print(account.currency)

不过,业务需要的属性最好在 __init__() 中统一初始化。这样,其他代码只要看到类的定义,就能知道一个实例通常包含哪些数据。

3. 方法

写在类中的函数叫作方法。实例方法的第一个参数通常是 self,通过它读取或修改当前实例:

methods.py
01
class Counter:
02
def __init__(self):
03
self.value = 0
04
05
def increase(self, amount=1):
06
self.value += amount
07
08
def current(self):
09
return self.value
10
11
counter = Counter()
12
counter.increase()
13
counter.increase(4)
14
print(counter.current()) # 5

调用 counter.increase(4) 时,Python 会自动把 counter 作为 self 传给方法。可以把它近似理解为 Counter.increase(counter, 4),所以方法内部才能访问 self.value。

方法不仅可以读取属性,也可以返回计算结果。把和对象状态有关的操作放进方法,调用方就不需要了解内部实现:

order.py
01
class Order:
02
def __init__(self, price, quantity):
03
self.price = price
04
self.quantity = quantity
05
06
def total(self):
07
return self.price * self.quantity
08
09
def summary(self):
10
return f"共 {self.quantity} 件,合计 {self.total()} 元"
11
12
order = Order(30, 2)
13
print(order.summary()) # 共 2 件,合计 60 元

如果希望 print(order) 时显示更有意义的内容,可以实现 __str__():

printable.py
1
class User:
2
def __init__(self, name):
3
self.name = name
4
5
def __str__(self):
6
return f"User(name={self.name})"
7
8
print(User("小明")) # User(name=小明)

以双下划线包围的 __init__、__str__ 等方法叫特殊方法。它们通常由 Python 在特定时机自动调用,不需要像普通方法那样直接使用。

类中还可以定义类方法和静态方法。它们与实例方法的区别在于,自动接收的第一个参数不同:

方法类型装饰器第一个参数适合处理
实例方法无self当前实例的数据
类方法@classmethodcls整个类的数据或创建实例
静态方法@staticmethod无与类主题相关、但不依赖实例和类状态的工具逻辑
method-types.py
01
class User:
02
def __init__(self, name):
03
self.name = name
04
05
def rename(self, name):
06
self.name = name
07
08
@classmethod
09
def guest(cls):
10
return cls("游客")
11
12
@staticmethod
13
def is_valid_name(name):
14
return bool(name.strip())
15
16
user = User.guest()
17
user.rename("访客")
18
print(user.name) # 访客
19
print(User.is_valid_name("小明")) # True

guest() 是类方法,可以通过 cls 创建当前类的实例,因此常被用作「替代构造方法」。is_valid_name() 不需要读取实例或类的数据,只是一个和用户相关的检查函数,所以可以定义为静态方法。

除了 __str__(),还可以实现 __repr__() 和 __eq__()。前者主要用于调试时展示对象,后者用于定义两个实例如何比较:

compare.py
01
class User:
02
def __init__(self, name):
03
self.name = name
04
05
def __str__(self):
06
return self.name
07
08
def __repr__(self):
09
return f"User(name={self.name!r})"
10
11
def __eq__(self, other):
12
return isinstance(other, User) and self.name == other.name
13
14
first = User("小明")
15
second = User("小明")
16
17
print(str(first)) # 小明
18
print(repr(first)) # User(name='小明')
19
print(first == second) # True

str() 更适合面向用户的内容,repr() 更适合调试。默认情况下,两个不同实例即使属性相同,使用 == 也不会被认为相等;实现 __eq__() 后,类可以定义自己的比较规则。is 判断的是不是同一个对象,不会调用 __eq__()。

4. 私有

Python 没有像某些语言那样强制禁止访问的私有方法。下划线主要是给开发者的访问约定:

写法含义是否可以从类外调用
method()公共方法,供外部正常使用可以
_method()内部方法,表示不建议外部直接使用可以,但不推荐
__method()触发名称改写,主要用于避免子类意外覆盖仍然可以通过改写后的名称访问
__method__()特殊方法,由 Python 在特定时机调用通常不手动调用

单个下划线最常见。它不会阻止访问,只是告诉调用方「这是类的内部实现,公开接口发生变化时不一定保持兼容」:

internal-method.py
01
class Account:
02
def __init__(self, owner):
03
self.owner = owner
04
self._balance = 0
05
06
def deposit(self, amount):
07
if not self._is_valid_amount(amount):
08
raise ValueError("存款金额必须大于 0")
09
10
self._balance += amount
11
12
def _is_valid_amount(self, amount):
13
return amount > 0
14
15
account = Account("小明")
16
account.deposit(100)
17
print(account._balance) # 可以访问,但业务代码不应该依赖这个内部属性

deposit() 是对外提供的操作,_is_valid_amount() 只是它内部使用的辅助方法。外部代码技术上可以调用 _is_valid_amount(),但应该优先使用类提供的公共方法。

两个连续的前置下划线会触发名称改写(name mangling)。例如,__cleanup() 会在类内部被改写为类似 _Session__cleanup,这样子类定义同名方法时,不容易意外覆盖父类的内部实现:

name-mangling.py
01
class Session:
02
def __cleanup(self):
03
print("清理会话")
04
05
def close(self):
06
self.__cleanup()
07
08
session = Session()
09
session.close() # 清理会话
10
# session.__cleanup() # AttributeError:原始名称不能直接访问

名称改写不是安全机制,也不能真正阻止访问。它主要用于减少子类和父类之间的命名冲突。最后要特别区分:__init__()、__str__() 两端都有下划线,它们是特殊方法,不是私有方法。

5. 属性

实例属性属于某一个对象,类属性则属于整个类。类属性可以被所有实例读取:

class-attributes.py
01
class Article:
02
category = "Python"
03
04
def __init__(self, title):
05
self.title = title
06
07
first = Article("变量")
08
second = Article("函数")
09
10
print(first.category) # Python
11
print(second.category) # Python

如果实例没有自己的同名属性,Python 会继续到类上查找。因此,类属性适合保存所有实例共享的常量或默认配置:

class-constant.py
1
class Request:
2
default_timeout = 30
3
4
def __init__(self, path):
5
self.path = path
6
7
request = Request("/users")
8
print(request.default_timeout)
9
print(Request.default_timeout)

不要把可变对象(例如列表、字典)作为类属性来保存每个实例自己的状态,否则所有实例可能会共享同一个对象:

shared-list.py
1
class BadPlaylist:
2
songs = []
3
4
first = BadPlaylist()
5
second = BadPlaylist()
6
first.songs.append("晴天")
7
8
print(second.songs) # ['晴天'],两个实例共享同一个列表

正确做法是把可变属性放到 __init__() 中,让每个实例创建自己的列表:

own-list.py
01
class Playlist:
02
def __init__(self):
03
self.songs = []
04
05
first = Playlist()
06
second = Playlist()
07
first.songs.append("晴天")
08
09
print(first.songs) # ['晴天']
10
print(second.songs) # []

如果直接公开属性,外部代码可以写入任意值。@property 可以让一个方法以属性的方式被读取,并在赋值时加入校验:

property.py
01
class User:
02
def __init__(self, name):
03
self.name = name
04
05
@property
06
def name(self):
07
return self._name
08
09
@name.setter
10
def name(self, value):
11
value = value.strip()
12
if not value:
13
raise ValueError("用户名不能为空")
14
15
self._name = value
16
17
user = User(" 小明 ")
18
print(user.name) # 小明
19
user.name = "小红"

调用方仍然使用 user.name,不需要写成 user.name()。只有定义 @property 而没有定义 setter 时,这个属性才是只读的。@property 适合把校验、格式转换和兼容旧接口的逻辑封装在类内部。

6. 组合

类可以通过组合表达「一个对象使用另一个对象」。例如,一个聊天服务可以持有模型名称,并负责组装请求数据:

chat-service.py
01
class ChatService:
02
def __init__(self, model_name="deepseek-chat"):
03
self.model_name = model_name
04
05
def build_request(self, question):
06
return {
07
"model": self.model_name,
08
"messages": [
09
{"role": "user", "content": question},
10
],
11
}
12
13
service = ChatService()
14
request = service.build_request("请介绍一下 Python。")
15
print(request)

在真实的 FastAPI 项目中,接口函数可以接收请求,再调用 ChatService 的方法;在 LangChain 项目中,服务类也可以持有模型实例、提示词和工具。这样,路由负责 HTTP,服务类负责业务,模型调用不会全部堆在接口函数里。

如果一个类主要用于保存数据,可以使用标准库中的 dataclass。它会根据类型注解自动生成常用的 __init__()、__repr__() 和 __eq__():

dataclass.py
01
from dataclasses import dataclass
02
03
@dataclass
04
class ChatRequest:
05
question: str
06
model: str = "deepseek-chat"
07
08
request = ChatRequest("请介绍一下 Python。")
09
print(request)
10
print(request == ChatRequest("请介绍一下 Python。")) # True

dataclass 适合数据结构清晰、行为较少的对象,例如 FastAPI 的请求配置或 LangChain 的运行参数。如果一个类包含较多业务行为,仍然可以手写普通类和方法。

类之间还可以通过继承复用代码。子类会获得父类的方法和属性,也可以覆盖某个方法:

inheritance.py
01
class Message:
02
def __init__(self, content):
03
self.content = content
04
05
def render(self):
06
return self.content
07
08
class SystemMessage(Message):
09
def render(self):
10
return f"[系统] {self.content}"
11
12
message = SystemMessage("你是一名温和的助手。")
13
print(message.render()) # [系统] 你是一名温和的助手。

子类需要复用父类的初始化逻辑时,可以调用 super():

super.py
01
class User:
02
def __init__(self, name):
03
self.name = name
04
05
class Admin(User):
06
def __init__(self, name, level):
07
super().__init__(name)
08
self.level = level
09
10
admin = Admin("小明", 2)
11
print(admin.name) # 小明
12
print(admin.level) # 2

继承适合表达明确的「是一种」关系,例如 Admin 是一种 User。如果两个对象只是互相协作,优先考虑组合,避免为了复用几行代码创建过深的继承层级。

可以使用 isinstance() 检查一个对象是否属于某个类,使用 issubclass() 检查两个类之间是否存在继承关系:

type-check.py
01
class User:
02
pass
03
04
class Admin(User):
05
pass
06
07
admin = Admin()
08
09
print(isinstance(admin, Admin)) # True
10
print(isinstance(admin, User)) # True,子类实例也属于父类类型
11
print(issubclass(Admin, User)) # True

type(admin) is Admin 只判断对象的直接类型,而 isinstance(admin, User) 还会考虑继承关系。业务代码需要兼容子类时,通常优先使用 isinstance()。

正在验证登录状态
请稍候,验证完成后将继续显示文章内容