【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 小时前
【FDE系列】阶段2:Day 31:SQL 基础 — 增删改查一把梭
人工智能·python·fde
leihefeng1 小时前
PX04-用 Python 读 Excel 画折线图,还能直接插回 Excel 文件
python·excel
Mr.朱鹏1 小时前
Docker三剑客实战指南:Docker、Dockerfile 和 Docker Compose
python·docker·devops·dockerfile
0566461 小时前
用大模型构建答疑机器人:从 API 调用到上下文工程
python·学习·agent
haluhalu.1 小时前
初识 Protobuf:微服务跨语言通信的一份契约
java·c语言·开发语言·c++·python
Liaiyang661 小时前
# 自研 AST 容错初筛工具:横向评测 Django、PyTorch、TensorFlow 三大开源 Python 框架异常收容风险
python·测试工具·自动化·开源软件·代码规范·devops·代码复审
空奈qwq1 小时前
机器学习入门:从核心概念到建模全流程
人工智能·python·机器学习
IvanCodes1 小时前
Python 基础语法(六):字典与集合
python
lie..1 小时前
30天从零开始学AI应用开发(Day 4):Python 极速入门(上):够用就行,别啃书
人工智能·python·大模型