这是我的第484篇原创文章。
一、引言
这是 Python 面向对象中的经典问题。三者最核心的区别在于:第一个参数是谁、能否访问实例、能否访问类。
三者都是定义在类里的函数。装饰器只是改变了函数的调用绑定方式(descriptor 协议):
-
实例方法绑定到实例
-
类方法绑定到类
-
静态方法不做绑定,就是个普通函数
一句话总结
-
实例方法 :操作"这个对象"的数据 → 用
self -
类方法 :操作"这类对象"的数据、做工厂方法 → 用
cls -
静态方法:只是逻辑上属于这个类的工具函数,不碰实例也不碰类 → 无参数
| 特性 | 实例方法 | 类方法 (@classmethod) | 静态方法 (@staticmethod) |
|---|---|---|---|
| 第一个参数 | self (实例对象) |
cls (类对象) |
无 |
| 能否访问实例属性 | ✅ 可以 | ❌ 不可以 | ❌ 不可以 |
| 能否访问类属性 | ✅ 可以 | ✅ 可以 | ✅ 可以(需通过类名) |
| 调用方式 | 必须用实例调用 | 类或实例都能调用 | 类或实例都能调用 |
| 装饰器 | 无(默认) | @classmethod |
@staticmethod |
| 继承行为 | 子类实例调用时,self 是子类实例 |
子类调用时,cls 是子类本身 |
无特殊行为 |
二、实现过程
2.1 能否访问实例属性/能否访问类属性
案例:
python
class MyClass:
class_var = "我是类变量"
def __init__(self, value):
self.instance_var = value # 实例变量
# ============ 实例方法 ============
def instance_method(self):
print(f"访问实例变量: {self.instance_var}")
print(f"访问类变量: {self.class_var}")
# ============ 类方法 ============
@classmethod
def class_method(cls):
print(f"访问类变量: {cls.class_var}")
print(f"cls 是: {cls}")
# print(self.instance_var) # ❌ 错误:不能访问实例变量
# ============ 静态方法 ============
@staticmethod
def static_method():
print("静态方法 - 与类和实例都无关")
print(f"访问类变量: {MyClass.class_var}") # 需要显式指定类名
# print(self.instance_var) # ❌ 错误:不能访问实例变量
2.2 实例方法 - 最常用的方法
案例:
python
class Person:
def __init__(self, name):
self.name = name
def greet(self): # 操作实例数据
return f"Hello, I'm {self.name}"
def change_name(self, new_name): # 修改实例状态
self.name = new_name
适用场景:需要访问或修改实例的状态(属性)
2.3 类方法 - 操作类本身
类方法:
python
class Configuration:
default_config = {"debug": False, "timeout": 30}
@classmethod
def reset_defaults(cls):
cls.default_config = {"debug": False, "timeout": 30}
@classmethod
def create_with_debug(cls):
config = cls() # 创建实例
config.default_config["debug"] = True
return config
适用场景:
-
工厂方法(替代构造函数),用 cls(...) 创建实例
-
修改类级别的配置
-
需要被子类继承并正确识别子类类型
2.4 静态方法 - 与类相关的工具函数
案例:
python
class MathUtils:
@staticmethod
def add(a, b):
return a + b
@staticmethod
def is_positive(n):
return n > 0
def calculate(self, x, y):
# 在实例方法中使用静态方法
if self.is_positive(x): # 通过 self 调用
return self.add(x, y)
适用场景:
-
逻辑上属于类,但不需要访问类或实例
-
工具函数、辅助函数
-
纯函数(输入确定,输出确定)
2.5 综合案例
示例代码:银行账户系统
python
class BankAccount:
# 类属性:所有账户共享
bank_name = "宇宙银行"
interest_rate = 0.03 # 年利率 3%
_total_accounts = 0 # 账户总数(私有类属性)
def __init__(self, owner, balance=0):
self.owner = owner # 实例属性:户主
self.balance = balance # 实例属性:余额
BankAccount._total_accounts += 1
self.account_id = f"ACC-{BankAccount._total_accounts:06d}"
# ========== 实例方法:操作具体账户 ==========
def deposit(self, amount):
"""存钱------只能对具体账户操作"""
if amount <= 0:
raise ValueError("存款金额必须大于0")
self.balance += amount
return self
def withdraw(self, amount):
"""取钱------只能对具体账户操作"""
if amount > self.balance:
raise ValueError("余额不足")
self.balance -= amount
return self
def transfer(self, other, amount):
"""转账------涉及两个具体账户"""
self.withdraw(amount)
other.deposit(amount)
return self
def info(self):
"""查看账户信息"""
return f"[{self.account_id}] {self.owner}: ¥{self.balance:.2f}"
# ========== 类方法:操作类级别数据 ==========
@classmethod
def get_total_accounts(cls):
"""查看开了多少个账户------不依赖某个具体账户"""
return cls._total_accounts
@classmethod
def set_interest_rate(cls, new_rate):
"""央行调整利率------影响所有账户"""
cls.interest_rate = new_rate
print(f"利率已调整至 {new_rate*100}%")
@classmethod
def create_savings_account(cls, owner, initial_deposit):
"""工厂方法:创建储蓄账户(带最低存款要求)"""
if initial_deposit < 1000:
raise ValueError("储蓄账户最低存款 ¥1000")
return cls(owner, initial_deposit)
# ========== 静态方法:独立工具函数 ==========
@staticmethod
def validate_id_card(id_number):
"""验证身份证号------和任何账户/类数据无关"""
if len(id_number) != 18:
return False
# 简化校验:最后一位校验码
weights = [7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2]
check_codes = "10X98765432"
try:
total = sum(int(id_number[i]) * weights[i] for i in range(17))
return check_codes[total % 11] == id_number[17].upper()
except ValueError:
return False
@staticmethod
def format_currency(amount):
"""格式化金额------纯工具函数"""
return f"¥{amount:,.2f}"
# ========== 实际使用 ==========
# 1. 实例方法:操作具体账户
alice = BankAccount("Alice", 5000)
bob = BankAccount("Bob", 3000)
alice.deposit(2000).withdraw(500) # 链式调用
alice.transfer(bob, 1000)
print(alice.info()) # [ACC-000001] Alice: ¥5500.00
print(bob.info()) # [ACC-000002] Bob: ¥4000.00
# 2. 类方法:不创建账户也能查/改类级别信息
print(BankAccount.get_total_accounts()) # 2
BankAccount.set_interest_rate(0.035) # 利率已调整至 3.5%
# 通过实例也能调用类方法(但不常见)
print(bob.get_total_accounts()) # 2
# 工厂方法创建账户
charlie = BankAccount.create_savings_account("Charlie", 5000)
# 3. 静态方法:完全独立的工具,只是挂在类下面
print(BankAccount.validate_id_card("11010519491231002X")) # True
print(BankAccount.format_currency(1234567.89)) # ¥1,234,567.89
# 实例也能调静态方法(但不推荐,语义上容易混淆)
print(alice.format_currency(100)) # ¥100.00
分析:

总结
-
实例方法:最常用的方法,操作对象数据,必须有实例
-
类方法:操作类本身,常用于工厂模式,自动处理继承
-
静态方法:纯粹的工具函数,放在类中是为了代码组织
作者简介:
读研期间发表6篇SCI数据挖掘相关论文,现在某研究院从事数据算法相关科研工作,结合自身科研实践经历不定期分享关于Python、机器学习、深度学习、人工智能系列基础知识与应用案例。致力于只做原创,以最简单的方式理解和学习,关注我一起交流成长。需要数据集和源码的小伙伴可以关注底部公众号添加作者微信。