在理解类和对象的基本关系之后,接下来的关键问题是:哪些数据属于类本身,哪些属于每个实例?哪些行为需要绑定到实例,哪些又属于类层级的功能? 这一节理清实例属性、类属性、实例方法、类方法和静态方法的区别和选型。
实例属性
定义:绑定在具体实例对象上的属性,每个实例各自拥有一份独立的数据。通常在 init 中通过 self.属性名 赋值定义。
特点:
- 每个实例的值互不影响。
- 实例属性是描述对象“个体差异”的核心载体。
示例:
class Dog:
def __init__(self, name, age):
self.name = name # 实例属性
self.age = age
dog1 = Dog("旺财", 3)
dog2 = Dog("来福", 5)
print(dog1.name) # 旺财
print(dog2.name) # 来福
这里 name 和 age 就是实例属性,每只狗有自己不同的名字和年龄。
类属性
定义:直接定义在类体中、不在任何方法内的属性,属于类对象本身。所有实例共享同一份数据,通过 类名.属性 或 实例.属性 均可访问。
特点:
- 内存中只存一份,节省空间。
- 适合存储不应该按实例变化的常量、配置、统计值等。
示例:
class Dog:
species = "Canis familiaris" # 类属性
def __init__(self, name):
self.name = name
dog1 = Dog("旺财")
dog2 = Dog("来福")
print(Dog.species) # Canis familiaris
print(dog1.species) # Canis familiaris (通过实例也可读)
print(dog2.species) # Canis familiaris
所有狗都属于同一个物种,所以 species 定义成类属性很合理。
注意:通过实例对类属性赋值,会在该实例上创建一个同名的实例属性,而不会修改类属性本身。修改应从 Dog.species = … 进行。
实例方法
定义:第一个参数为 self(约定名称,指代当前实例)的普通方法。它操作实例属性和其他方法,是面向对象编程中最常用的方法类型。
特点:
- 必须通过实例调用(
instance.method()),self会自动传递实例。 - 既能访问实例属性,也能访问类属性和其他方法。
示例:
class Dog:
def __init__(self, name):
self.name = name
def bark(self):
print(f"{self.name} 汪汪叫")
d = Dog("旺财")
d.bark() # 旺财 汪汪叫
类方法
定义:用 @classmethod 装饰的方法,第一个参数为 cls(约定名称,指代类本身)。它操作类属性或提供替代构造函数。
特点:
- 通过类或实例调用均可,
cls会自动传递类对象。 - 不能访问实例属性(因为没
self),专门处理类级别的逻辑。
典型用途:
- 工厂方法:定义不同的初始化方式。
- 修改类属性:在类级别维护状态。
示例:
class Dog:
species = "Canis familiaris"
def __init__(self, name):
self.name = name
@classmethod
def change_species(cls, new_species):
cls.species = new_species
@classmethod
def from_birth_year(cls, name, birth_year):
# 工厂方法,根据出生年计算年龄来创建实例
age = 2025 - birth_year
return cls(name)
# 修改类属性
Dog.change_species("Canis lupus")
print(Dog.species) # Canis lupus
# 工厂方法创建实例
dog = Dog.from_birth_year("旺财", 2022)
print(dog.name)
from_birth_year 是一个典型的备选构造器,它返回一个 Dog 实例,封装了基于出生年创建的逻辑。
静态方法
定义:用 @staticmethod 装饰的方法,没有 self 或 cls 参数。本质上就是放在类命名空间里的普通函数,与类有逻辑关联但不需要操作类或实例的数据。
特点:
- 通过类或实例调用均可。
- 不能直接访问实例属性或类属性(除非显式通过类名)。
典型用途:
- 工具函数,逻辑上属于该类职责但不依赖具体状态。
- 保持命名空间整洁,避免模块级函数散落。
示例:
class Dog:
@staticmethod
def is_valid_name(name):
return isinstance(name, str) and len(name) > 0
# 调用
print(Dog.is_valid_name("")) # False
print(Dog.is_valid_name("旺财")) # True
这里判断名字是否合法不依赖任何狗实例,也不依赖狗类本身,但属于“狗类相关”的逻辑,放在类内比放在模块级更清晰。
快速对照与选型指南
| 类型 | 绑定对象 | 第一个参数 | 装饰器 | 何时使用 |
|------------|----------|------------|----------------|----------|
| 实例属性 | 实例 | 无 | 无 | 每个实例各自不同的数据 |
| 类属性 | 类 | 无 | 无 | 所有实例共享的数据、常量 |
| 实例方法 | 实例 | self | 无 | 需要操作实例属性的行为 |
| 类方法 | 类 | cls | @classmethod | 操作类属性、工厂方法 |
| 静态方法 | 无严格绑定 | 无 | @staticmethod| 与类逻辑相关但无需访问类或实例状态的工具函数 |
理解这五种成员的区别与联系,是写出清晰、可维护面向对象代码的基础。在实际项目中,你会频繁在它们之间根据职责做合理划分。