证券之星消息,根据天眼查APP数据显示中联重科(000157)新获得一项实用新型专利授权,专利名为“导料溜槽”,专利申请号为CN20242256776...
2025-08-26 0
在掌握了 OOP 三大核心特性(封装、继承、多态)后,我们需要进一步学习 Python 中类的进阶方法(类方法、静态方法)和实用设计模式。类方法和静态方法拓展了类的功能边界,分别用于 “操作类属性” 和 “提供工具函数”;而设计模式则是 OOP 思想的经典实践,能帮助我们解决复杂场景下的代码设计问题。本章将从类方法、静态方法的语法与应用,到单例模式、工厂模式的实现,逐步提升你的 OOP 实战能力。
在之前的学习中,我们接触的主要是 “实例方法”—— 依赖于对象,第一个参数必须是self,用于操作实例属性。而类方法是依赖于 “类” 的方法,通过@classmethod装饰器定义,第一个参数是cls(代表当前类,是 Python 的约定,不可省略),主要用于操作类属性或创建类的实例。
class 类名:
# 类属性(类方法通常操作类属性)
类属性名 = 初始值
# 类方法:用@classmethod装饰,第一个参数是cls
@classmethod
def 类方法名(cls, 参数1, 参数2, ...):
# 方法逻辑:可通过cls访问类属性或调用其他类方法
cls.类属性名 = 新值 # 修改类属性
return 结果
假设我们需要统计Student类创建的实例数量,可通过类属性count记录,并用类方法get_instance_count获取统计结果:
class Student:
# 类属性:统计实例数量,初始值为0
count = 0
def __init__(self, name, age):
self.name = name
self.age = age
# 每次创建实例,类属性count加1(通过类名访问类属性)
Student.count += 1
# 类方法:获取实例数量(操作类属性count)
@classmethod
def get_instance_count(cls):
# 通过cls访问类属性(cls等价于Student)
return f"Student类共创建了{cls.count}个实例"
# 类方法:修改类属性(批量更新类级别的配置)
@classmethod
def reset_count(cls):
cls.count = 0
return "实例计数器已重置为0"
# 1. 创建3个Student实例
student1 = Student("小明", 20)
student2 = Student("小红", 19)
student3 = Student("小刚", 21)
# 2. 通过“类名”调用类方法(推荐)
print(Student.get_instance_count()) # 输出:Student类共创建了3个实例
# 3. 通过“对象”调用类方法(不推荐,语法允许但逻辑上不符合类方法的设计意图)
print(student1.get_instance_count()) # 输出:Student类共创建了3个实例
# 4. 调用类方法重置计数器
print(Student.reset_count()) # 输出:实例计数器已重置为0
print(Student.get_instance_count()) # 输出:Student类共创建了0个实例
运行结果:
Student类共创建了3个实例
Student类共创建了3个实例
实例计数器已重置为0
Student类共创建了0个实例
类方法的核心作用:
类方法的另一个重要用途是 “创建类的实例”—— 当实例化过程需要复杂的逻辑(如从配置文件、数据库或 JSON 字符串中加载数据)时,可通过类方法封装实例化逻辑,对外提供简洁的调用接口(这种方式也称为 “工厂方法” 的简化版)。
import json
class Student:
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
def __str__(self):
return f"Student(name='{self.name}', age={self.age}, score={self.score})"
# 类方法:从JSON字符串创建Student实例(封装实例化逻辑)
@classmethod
def from_json(cls, json_str):
# 1. 解析JSON字符串为字典
try:
data = json.loads(json_str)
except json.JSONDecodeError as e:
raise ValueError(f"JSON解析失败:{e}")
# 2. 校验字典中是否包含必要的键
required_keys = ["name", "age", "score"]
if not all(key in data for key in required_keys):
raise KeyError(f"JSON数据缺少必要字段,需包含{required_keys}")
# 3. 创建并返回实例(cls等价于Student,cls(**data)等价于Student(**data))
return cls(** data)
# 1. 定义JSON字符串(模拟从文件或网络获取的数据)
json_data = '''
{
"name": "小明",
"age": 20,
"score": 95.5
}
'''
# 2. 通过类方法创建实例(无需手动解析JSON和校验数据)
try:
student1 = Student.from_json(json_data)
print("从JSON创建的实例:", student1) # 输出:从JSON创建的实例:Student(name='小明', age=20, score=95.5)
except (ValueError, KeyError) as e:
print("创建实例失败:", e)
# 3. 尝试用非法JSON创建实例(触发异常)
invalid_json = '{"name": "小红", "age": 19}' # 缺少score字段
try:
student2 = Student.from_json(invalid_json)
except KeyError as e:
print("创建实例失败:", e) # 输出:创建实例失败:JSON数据缺少必要字段,需包含['name', 'age', 'score']
运行结果:
从JSON创建的实例: Student(name='小明', age=20, score=95.5)
创建实例失败: JSON数据缺少必要字段,需包含['name', 'age', 'score']
优势分析:
静态方法是类中与 “类” 和 “实例” 都无关的方法,通过@staticmethod装饰器定义,没有默认参数(既无self也无cls)。它本质是一个 “普通函数”,只是被定义在类的命名空间下,用于提供与类相关但不依赖类属性或实例属性的工具函数。
class 类名:
# 静态方法:用@staticmethod装饰,无默认参数
@staticmethod
def 静态方法名(参数1, 参数2, ...):
# 方法逻辑:不访问类属性和实例属性,仅依赖传入的参数
pass
假设Student类需要一个 “验证手机号格式” 的工具函数 —— 该函数与具体的学生实例无关(不依赖name、age等属性),也与Student类的类属性无关,适合定义为静态方法:
import re
class Student:
def __init__(self, name, age, phone):
# 调用静态方法验证手机号格式
if not Student.is_valid_phone(phone):
raise ValueError("手机号格式无效(需为11位数字)")
self.name = name
self.age = age
self.phone = phone
# 静态方法:验证手机号格式(工具函数,与类/实例无关)
@staticmethod
def is_valid_phone(phone):
# 正则表达式:匹配11位数字
pattern = r'^1\d{10}$'
return isinstance(phone, str) and re.match(pattern, phone) is not None
# 1. 通过“类名”调用静态方法(推荐,直接作为工具函数使用)
print("验证手机号13800138000:", Student.is_valid_phone("13800138000")) # 输出:验证手机号13800138000:True
print("验证手机号123456:", Student.is_valid_phone("123456")) # 输出:验证手机号123456:False
# 2. 创建实例(手机号合法)
try:
student1 = Student("小明", 20, "13800138000")
print(f"创建成功:{student1.name},手机号:{student1.phone}") # 输出:创建成功:小明,手机号:13800138000
except ValueError as e:
print("创建失败:", e)
# 3. 创建实例(手机号非法)
try:
student2 = Student("小红", 19, "123456")
except ValueError as e:
print("创建失败:", e) # 输出:创建失败:手机号格式无效(需为11位数字)
运行结果:
验证手机号13800138000: True
验证手机号123456: False
创建成功:小明,手机号:13800138000
创建失败:手机号格式无效(需为11位数字)
静态方法的核心作用:
为了清晰区分三种方法的适用场景,我们从 “参数、依赖对象、访问权限、调用方式” 四个维度进行对比:
对比维度 | 实例方法(Instance Method) | 类方法(Class Method) | 静态方法(Static Method) |
第一个参数 | self(代表当前实例) | cls(代表当前类) | 无默认参数 |
依赖对象 | 依赖实例(必须通过实例调用,或手动传实例) | 依赖类(无需实例,通过类调用) | 不依赖类和实例(纯函数) |
访问权限 | 可访问实例属性和类属性 | 可访问类属性,不可访问实例属性 | 不可访问实例属性和类属性(仅用传入参数) |
调用方式 | 对象.方法名() 或 类名.方法名(实例) | 类名.方法名() 或 对象.方法名() | 类名.方法名() 或 对象.方法名() |
核心用途 | 操作实例属性,实现对象的行为 | 操作类属性,创建实例,类级工具逻辑 | 提供与类相关的纯工具函数 |
class Demo:
# 类属性
class_attr = "类属性值"
def __init__(self, instance_attr):
# 实例属性
self.instance_attr = instance_attr
# 1. 实例方法:依赖self,访问实例属性和类属性
def instance_method(self):
print(f"实例方法 - 实例属性:{self.instance_attr},类属性:{self.class_attr}")
# 2. 类方法:依赖cls,访问类属性,不可访问实例属性
@classmethod
def class_method(cls):
print(f"类方法 - 类属性:{cls.class_attr}")
# 错误:类方法无法访问实例属性(cls没有instance_attr)
# print(f"实例属性:{cls.instance_attr}")
# 3. 静态方法:无默认参数,不访问类属性和实例属性
@staticmethod
def static_method(a, b):
print(f"静态方法 - 传入参数的和:{a + b}")
# 错误:静态方法无法访问类属性和实例属性
# print(f"类属性:{cls.class_attr}")
# print(f"实例属性:{self.instance_attr}")
# 创建实例
demo = Demo("实例属性值")
# 调用三种方法
print("=== 调用实例方法 ===")
demo.instance_method() # 输出:实例方法 - 实例属性:实例属性值,类属性:类属性值
print("\n=== 调用类方法 ===")
Demo.class_method() # 输出:类方法 - 类属性:类属性值
print("\n=== 调用静态方法 ===")
Demo.static_method(3, 5) # 输出:静态方法 - 传入参数的和:8
运行结果:
=== 调用实例方法 ===
实例方法 - 实例属性:实例属性值,类属性:类属性值
=== 调用类方法 ===
类方法 - 类属性:类属性值
=== 调用静态方法 ===
静态方法 - 传入参数的和:8
在实际开发中,选择哪种方法需根据 “是否依赖类 / 实例属性” 来判断,具体建议如下:
设计模式是 OOP 开发中总结的 “可复用解决方案”,用于解决特定场景下的代码设计问题(如 “确保一个类只有一个实例”“统一创建不同子类的对象”)
。掌握设计模式能让你的代码更具可读性、可维护性和扩展性,避免重复 “造轮子”。本节将介绍两种最常用的设计模式:单例模式(确保类只有一个实例)和工厂模式(统一创建子类对象),并结合 Python 特性讲解实现方式。
在某些场景下,我们需要确保一个类只能创建一个实例(如数据库连接池、配置管理器、日志对象)—— 若创建多个实例,可能导致资源冲突(如多个数据库连接修改同一数据)或资源浪费(如重复创建重量级对象)。单例模式(Singleton Pattern)就是解决这类问题的经典方案。
__new__是 Python 中比__init__更早执行的方法,负责 “创建对象并分配内存”。通过重写__new__方法,控制对象的创建逻辑,确保只生成一个实例。
class Singleton:
# 类属性:存储唯一实例
_instance = None
def __new__(cls, *args, **kwargs):
# 1. 判断是否已创建实例:若未创建,调用父类__new__创建;若已创建,直接返回
if cls._instance is None:
# 调用object的__new__方法,创建对象并分配内存
cls._instance = super().__new__(cls)
# 2. 返回唯一实例(无论是否新创建)
return cls._instance
def __init__(self, name):
# 注意:若多次调用__init__,会重复初始化实例属性(需处理)
if not hasattr(self, "name"): # 仅当实例未初始化name时,才赋值
self.name = name
# 测试单例模式:多次创建对象,判断是否为同一个实例
s1 = Singleton("实例1")
s2 = Singleton("实例2")
# 1. 判断两个对象的内存地址是否相同(相同则为同一实例)
print(f"s1的内存地址:{id(s1)}") # 输出:s1的内存地址:2520858445648(示例值)
print(f"s2的内存地址:{id(s2)}") # 输出:s2的内存地址:2520858445648(与s1相同)
# 2. 查看实例属性(s2的name未覆盖s1的name,因hasattr判断避免重复初始化)
print(f"s1.name:{s1.name}") # 输出:s1.name:实例1
print(f"s2.name:{s2.name}") # 输出:s2.name:实例1
# 3. 验证是否为同一实例(is判断内存地址)
print(f"s1 is s2:{s1 is s2}") # 输出:s1 is s2:True
运行结果:
s1的内存地址:2520858445648
s2的内存地址:2520858445648
s1.name:实例1
s2.name:实例1
s1 is s2:True
关键逻辑:
通过装饰器包装类,在装饰器内部维护唯一实例,实现单例逻辑。这种方式的优势是 “解耦”—— 单例逻辑与类本身分离,可复用给多个类。
def singleton(cls):
# 字典:存储被装饰类的唯一实例(key:类,value:实例)
instances = {}
def wrapper(*args, **kwargs):
# 1. 若类未在instances中,创建实例并加入;否则直接返回已有实例
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return wrapper
# 用装饰器将普通类变为单例类
@singleton
class DatabaseConnection:
def __init__(self, host, port):
self.host = host
self.port = port
def connect(self):
print(f"连接数据库:{self.host}:{self.port}")
# 测试单例模式
db1 = DatabaseConnection("localhost", 3306)
db2 = DatabaseConnection("127.0.0.1", 3306) # 传入不同参数,但仍返回同一实例
print(f"db1 is db2:{db1 is db2}") # 输出:db1 is db2:True
db1.connect() # 输出:连接数据库:localhost:3306(db2的参数未生效,因实例已创建)
db2.connect() # 输出:连接数据库:localhost:3306
运行结果:
db1 is db2:True
连接数据库:localhost:3306
连接数据库:localhost:3306
优势:
Python 的模块具有 “导入时只加载一次” 的特性 —— 当一个模块被多次导入时,Python 只会创建一个模块对象,不会重复加载。利用这一特性,可将单例实例定义在模块中,实现单例模式。
步骤:
示例:
class ConfigManager:
def __init__(self):
# 模拟加载配置文件
self.config = {"debug": True, "timeout": 30}
def get_config(self, key):
return self.config.get(key)
# 模块中创建唯一实例
config_manager = ConfigManager()
# 第一次导入实例
from singleton_module import config_manager as cm1
# 第二次导入实例(与cm1是同一个)
from singleton_module import config_manager as cm2
# 验证是否为同一实例
print(f"cm1 is cm2:{cm1 is cm2}") # 输出:cm1 is cm2:True
# 调用实例方法(结果一致)
print(f"cm1获取debug配置:{cm1.get_config('debug')}") # 输出:cm1获取debug配置:True
print(f"cm2获取timeout配置:{cm2.get_config('timeout')}") # 输出:cm2获取timeout配置:30
运行结果:
cm1 is cm2:True
cm1获取debug配置:True
cm2获取timeout配置:30
优势:
在多继承或子类较多的场景下,直接通过子类名()创建对象会导致 “创建逻辑分散”—— 若后续子类名修改或新增子类,需修改所有创建对象的代码。工厂模式(Factory Pattern)通过定义一个 “工厂类”,统一负责所有子类对象的创建,外部只需告诉工厂 “要创建什么类型的对象”,无需关心具体创建逻辑。
假设我们需要开发一个支付系统,支持微信支付、支付宝支付、银联支付三种方式,每种支付方式的逻辑不同,但都需要 “发起支付” 的接口。通过工厂模式,可统一管理支付方式的创建逻辑。
from abc import ABC, abstractmethod
# 抽象产品:支付方式接口
class Payment(ABC):
@abstractmethod
def pay(self, amount):
"""抽象方法:发起支付,子类必须重写"""
pass
# 具体产品1:微信支付
class WeChatPayment(Payment):
def pay(self, amount):
return f"微信支付成功:{amount}元(订单号:WX{hash(self)}{amount})"
# 具体产品2:支付宝支付
class AlipayPayment(Payment):
def pay(self, amount):
return f"支付宝支付成功:{amount}元(订单号:ALIPAY{hash(self)}{amount})"
# 具体产品3:银联支付
class UnionPayPayment(Payment):
def pay(self, amount):
return f"银联支付成功:{amount}元(订单号:UNION{hash(self)}{amount})"
class PaymentFactory:
# 静态方法:根据支付类型创建对应的支付实例
@staticmethod
def create_payment(payment_type):
# 统一创建逻辑:根据参数返回不同子类实例
if payment_type.lower() == "wechat":
return WeChatPayment()
elif payment_type.lower() == "alipay":
return AlipayPayment()
elif payment_type.lower() == "unionpay":
return UnionPayPayment()
else:
raise ValueError(f"不支持的支付方式:{payment_type},支持的类型:wechat/alipay/unionpay")
# 1. 通过工厂类创建不同支付方式的实例(无需直接调用子类构造函数)
try:
# 创建微信支付实例
wechat_pay = PaymentFactory.create_payment("wechat")
# 创建支付宝支付实例
alipay_pay = PaymentFactory.create_payment("alipay")
# 创建银联支付实例
unionpay_pay = PaymentFactory.create_payment("unionpay")
# 2. 调用支付方法(多态体现:统一接口,不同实现)
print(wechat_pay.pay(100)) # 输出:微信支付成功:100元(订单号:WX1407152043190881024100)
print(alipay_pay.pay(200)) # 输出:支付宝支付成功:200元(订单号:ALIPAY1407152043190881088200)
print(unionpay_pay.pay(300)) # 输出:银联支付成功:300元(订单号:UNION1407152043190881152300)
# 3. 尝试创建不支持的支付方式(触发异常)
invalid_pay = PaymentFactory.create_payment("applepay")
except ValueError as e:
print("创建支付实例失败:", e) # 输出:创建支付实例失败:不支持的支付方式:applepay,支持的类型:wechat/alipay/unionpay
运行结果:
微信支付成功:100元(订单号:WX1407152043190881024100)
支付宝支付成功:200元(订单号:ALIPAY1407152043190881088200)
银联支付成功:300元(订单号:UNION1407152043190881152300)
创建支付实例失败:不支持的支付方式:applepay,支持的类型:wechat/alipay/unionpay
本实战将基于 7.3.2 节的支付场景,进一步完善工厂模式的实现,增加 “支付前校验” 和 “支付记录” 功能,具体需求如下:
import time
import random
from abc import ABC, abstractmethod
# 1. 定义Payment抽象类(抽象产品)
class Payment(ABC):
def __init__(self, app_id):
# 初始化支付平台的应用ID(每个支付实例对应一个app_id)
self.app_id = app_id
def validate_payment(self, amount):
"""
支付前校验:金额必须为正数
:param amount: 支付金额
:return: (is_valid: bool, message: str) 校验结果和提示信息
"""
if not isinstance(amount, (int, float)):
return False, "支付金额必须是整数或浮点数"
if amount <= 0:
return False, f"支付金额{amount}元无效,必须大于0"
return True, "校验通过"
@abstractmethod
def pay(self, amount):
"""抽象方法:发起支付,返回支付结果字符串"""
pass
# 2. 定义具体支付子类(具体产品)
class WeChatPayment(Payment):
def pay(self, amount):
# 生成订单号:WX + 时间戳(秒) + 6位随机数
timestamp = int(time.time())
random_num = random.randint(100000, 999999)
order_id = f"WX{timestamp}{random_num}"
# 返回支付结果(包含app_id,区分不同应用)
return f"微信支付(app_id:{self.app_id})成功:{amount}元,订单号:{order_id}"
class AlipayPayment(Payment):
def pay(self, amount):
# 生成订单号:ALIPAY + 时间戳(秒) + 6位随机数
timestamp = int(time.time())
random_num = random.randint(100000, 999999)
order_id = f"ALIPAY{timestamp}{random_num}"
# 返回支付结果
return f"支付宝支付(app_id:{self.app_id})成功:{amount}元,订单号:{order_id}"
# 3. 定义PaymentFactory工厂类
class PaymentFactory:
@staticmethod
def create_payment(payment_type, app_id):
"""
根据支付类型创建支付实例
:param payment_type: 支付类型("wechat"/"alipay")
:param app_id: 支付平台的应用ID
:return: 对应的支付实例(WeChatPayment/AlipayPayment)
"""
# 校验app_id合法性
if not isinstance(app_id, str) or len(app_id.strip()) == 0:
raise ValueError("app_id必须是非空字符串")
# 根据支付类型创建实例
payment_type = payment_type.lower()
if payment_type == "wechat":
return WeChatPayment(app_id.strip())
elif payment_type == "alipay":
return AlipayPayment(app_id.strip())
else:
raise ValueError(f"不支持的支付方式:{payment_type},仅支持wechat/alipay")
# 4. 定义PaymentRecord类(支付记录)
class PaymentRecord:
@staticmethod
def log_payment(payment_result):
"""
记录支付结果,包含当前时间
:param payment_result: 支付结果字符串(由pay方法返回)
"""
# 获取当前时间,格式:年-月-日 时:分:秒
current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
print(f"[{current_time}] 支付记录:{payment_result}")
# 5. 实战操作:执行支付流程
def execute_payment(payment, amount):
"""
执行支付流程:校验 → 支付 → 记录
:param payment: 支付实例(WeChatPayment/AlipayPayment)
:param amount: 支付金额
"""
# 步骤1:支付前校验
is_valid, message = payment.validate_payment(amount)
if not is_valid:
print(f"支付失败:{message}")
return
# 步骤2:发起支付
try:
payment_result = payment.pay(amount)
print(f"支付成功:{payment_result.split(',')[0]}") # 简化打印支付成功信息
except Exception as e:
print(f"支付异常:{e}")
return
# 步骤3:记录支付结果
PaymentRecord.log_payment(payment_result)
# 执行具体支付
if __name__ == "__main__":
print("=" * 60)
# 场景1:微信支付150元
try:
wechat_pay = PaymentFactory.create_payment("wechat", "wx123456")
execute_payment(wechat_pay, 150)
except ValueError as e:
print(f"微信支付初始化失败:{e}")
print("\n" + "=" * 60)
# 场景2:支付宝支付280元
try:
alipay_pay = PaymentFactory.create_payment("alipay", "alipay789")
execute_payment(alipay_pay, 280)
except ValueError as e:
print(f"支付宝支付初始化失败:{e}")
print("\n" + "=" * 60)
# 场景3:尝试支付负数金额(校验失败)
try:
wechat_pay2 = PaymentFactory.create_payment("wechat", "wx789012")
execute_payment(wechat_pay2, -50) # 金额为负,校验失败
except ValueError as e:
print(f"微信支付初始化失败:{e}")
print("\n" + "=" * 60)
# 场景4:尝试创建银联支付(不支持)
try:
unionpay_pay = PaymentFactory.create_payment("unionpay", "union123")
except ValueError as e:
print(f"银联支付初始化失败:{e}")
============================================================
支付成功:微信支付(app_id:wx123456)成功:150元
[2025-08-25 16:30:45] 支付记录:微信支付(app_id:wx123456)成功:150元,订单号:WX1756203445123456
============================================================
支付成功:支付宝支付(app_id:alipay789)成功:280元
[2025-08-25 16:30:45] 支付记录:支付宝支付(app_id:alipay789)成功:280元,订单号:ALIPAY1756203445654321
============================================================
支付失败:支付金额-50元无效,必须大于0
============================================================
银联支付初始化失败:不支持的支付方式:unionpay,仅支持wechat/alipay
通过本次工厂模式实战,我们实现了以下目标:
无需修改execute_payment、PaymentRecord等现有代码,完全符合 “开闭原则”。
本章的进阶内容为后续复杂项目开发奠定了基础,下一章我们将通过 “学生信息管理系统” 的综合实战,整合 OOP 的核心特性与进阶技巧,实现一个完整的小型项目。
相关文章
证券之星消息,根据天眼查APP数据显示中联重科(000157)新获得一项实用新型专利授权,专利名为“导料溜槽”,专利申请号为CN20242256776...
2025-08-26 0
亲,这款游戏可以开挂的,确实是有挂的,很多玩家在这款游戏中打牌都会发现很多用户的牌特别好,总是好牌,而且好像能看到-人的牌一样。所以很多小伙伴就怀疑这...
2025-08-26 0
证券之星消息,根据天眼查APP数据显示沃森生物(300142)新获得一项实用新型专利授权,专利名为“一种可倾倒可导流的废液收集装置”,专利申请号为CN...
2025-08-26 0
今天给各位分享途游四川麻将有挂吗的知识,其中也会对途游四川麻将怎么样进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!途游麻将电脑...
2025-08-26 0
您好:这款游戏是可以开挂的,软件加微信【添加图中微信】确实是有挂的,很多玩家在这款游戏中打牌都会发现很多用户的牌特别好,总是好牌,而且好像能看到其他人...
2025-08-26 0
发表评论