Python3 静态方法和类方法:面向对象编程的高级工具
Python 的 @classmethod 和 @staticmethod:什么时候用哪个,一次说清楚
写 Python 类的时候,除了普通的实例方法,还有两种带装饰器的方法:@classmethod 和 @staticmethod。
刚接触的时候很容易搞混——“不都是类里的方法吗,为什么还要分三种?”
其实区别很简单:实例方法要操作对象,类方法要操作类,静态方法只是”恰好放在这个类里的普通函数”。
先复习一下实例方法:带 self 的那个
class Person:
species = "人类"
def __init__(self, name):
self.name = name
def greet(self):
return f"你好,我是 {self.name}"
实例方法的特点:
- 第一个参数是
self,指向具体的对象 - 能访问对象的属性(
self.name) - 能访问类的属性(
self.species或Person.species) - 必须通过对象调用
实例方法处理的是”每个对象各自不同的数据”,比如每个人的名字不同。
类方法:@classmethod,操作的是类本身
类方法的第一个参数是 cls,指向类本身,而不是某个具体对象。
class Person:
count = 0
def __init__(self, name):
self.name = name
Person.count += 1
def get_count(cls):
return cls.count # 返回总人数,跟具体对象无关
# 通过类名调用
Person.get_count() # 0
类方法能访问和修改类属性(所有对象共享的数据),但不能访问实例属性(每个对象各自的数据)。
什么时候用类方法?三种最常见的场景:
场景1:工厂方法——用不同方式创建对象
from datetime import datetime
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
def from_string(cls, date_str):
"""从 '2024-01-15' 这样的字符串创建日期对象"""
year, month, day = map(int, date_str.split("-"))
return cls(year, month, day)
def today(cls):
"""创建今天的日期"""
now = datetime.now()
return cls(now.year, now.month, now.day)
# 用不同方式创建
d1 = Date.from_string("2024-01-15")
d2 = Date.today()
这种”替代构造函数”的模式,是类方法最典型的用途。
场景2:操作类属性(共享状态)
class GameConfig:
difficulty = "normal"
def set_difficulty(cls, level):
valid = ["easy", "normal", "hard"]
if level in valid:
cls.difficulty = level
def get_difficulty(cls):
return cls.difficulty
# 修改的是类的属性,所有地方都生效
GameConfig.set_difficulty("hard")
print(GameConfig.get_difficulty()) # hard
场景3:继承场景下,cls 会自动指向子类
class Animal:
def create(cls, name):
return cls(name)
class Dog(Animal):
def __init__(self, name):
self.name = name
class Cat(Animal):
def __init__(self, name):
self.name = name
# cls 会指向调用时的类
dog = Dog.create("旺财") # cls = Dog
cat = Cat.create("咪咪") # cls = Cat
如果在类方法里硬编码了类名(比如 return Animal(name)),子类调用的时候就会返回父类的实例,而不是子类的。用 cls 而不是写死类名,是类方法支持继承的关键。
静态方法:@staticmethod,就是普通函数放在类里
静态方法没有 self 也没有 cls,它就是个普通函数,只是恰好在类的命名空间里。
class MathUtils:
def add(a, b):
return a + b
MathUtils.add(2, 3) # 5
静态方法不能访问实例属性,也不能访问类属性(除非把类名传进去,但那样很别扭)。
什么时候用静态方法?它的价值在于”归类”——把相关但不需要访问类/实例状态的函数放在一起。
场景1:工具函数
class Validator:
def is_email(email):
import re
pattern = r'^[\w\.-]+@[\w\.-]+\.\w+$'
return bool(re.match(pattern, email))
def is_phone(phone):
import re
return bool(re.match(r'^1[3-9]\d{9}$', phone))
# 用类名调用,语义清晰
Validator.is_email("test@example.com")
Validator.is_phone("13812345678")
如果把这两个函数放在模块顶层也行,但放在 Validator 类里,代码组织更清晰——别人一看就知道”这些是验证相关的工具”。
场景2:数据转换
class UnitConverter:
def celsius_to_fahrenheit(c):
return c * 9/5 + 32
def km_to_miles(km):
return km * 0.621371
def bytes_to_human(size):
for unit in ['B', 'KB', 'MB', 'GB']:
if size < 1024:
return f"{size:.1f} {unit}"
size /= 1024
return f"{size:.1f} TB"
UnitConverter.celsius_to_fahrenheit(25) # 77.0