【Python程序开发系列】实例方法、类方法(@classmethod)、静态方法(@staticmethod)有什么区别

这是我的第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

分析:

总结

  1. 实例方法:最常用的方法,操作对象数据,必须有实例

  2. 类方法:操作类本身,常用于工厂模式,自动处理继承

  3. 静态方法:纯粹的工具函数,放在类中是为了代码组织

作者简介:

读研期间发表6篇SCI数据挖掘相关论文,现在某研究院从事数据算法相关科研工作,结合自身科研实践经历不定期分享关于Python、机器学习、深度学习、人工智能系列基础知识与应用案例。致力于只做原创,以最简单的方式理解和学习,关注我一起交流成长。需要数据集和源码的小伙伴可以关注底部公众号添加作者微信。

相关推荐
全栈练习生1 分钟前
模型上下文协议(MCP)
python·ai
泡海椒25 分钟前
JQuick-Excel 多字段 TRANSFORM:让当前行字段、JContext 与展示列各归其位
开发语言·python·excel
高洁0131 分钟前
智能博弈背景下中国AI国防建设的战略价值
人工智能·python·深度学习·django·tornado
han688937 分钟前
selenium之实战
笔记·python·selenium·测试工具·自动化
计算机毕业编程指导师43 分钟前
【计算机大数据毕业设计选题】基于Hadoop+Spark的人工智能社交媒体情绪数据分析系统源码 毕业设计 选题推荐 毕设选题 数据分析 机器学习
大数据·人工智能·hadoop·计算机·毕业设计·课程设计·社交媒体
yumgpkpm44 分钟前
(CDH 7)CDP Private Cloud Base 7.3.1 → Acceldata ODP 3.3.6.4 引擎迁移风险评估表
服务器·人工智能·hadoop·python·华为·zookeeper·hbase
刘天远1 小时前
Agent成本核算实现:事件表、状态分布与Python归集
前端·数据库·人工智能·python
Python图像识别1 小时前
40-【2027毕设】YOLO11面部口罩检测识别系统 - Python完整源码+PyQt5界面+训练模型+数据集
python·qt·课程设计
赵大仁1 小时前
开源清单怎么维护才不烂掉?GitHub Actions 每周查死链 + 软 404 检测实录
python·ci/cd·自动化·开源项目·踩坑·github actions·json schema
玩AI的奶茶1 小时前
24GB 显存能跑多大的模型?参数量、精度与显存占用对照表
人工智能·python·算法·ai·aigc·gpu算力·算力租赁