这种“知道步骤但写不出来”的情况非常常见,核心原因是对“类的参数设计”和“类之间的协作关系”还没有形成清晰的逻辑链。其实参数设计有明确的规律——每个类的参数都应该服务于它的“核心职责”。我们用“从职责倒推参数”的方法,一步步帮你落地这两个类的设计。
第一步:明确每个类的“核心职责”(先想清楚“它要干什么”)
在这个场景中:
-
Account
类的核心职责:
管理账户余额的变动(存款),并保证多线程操作时的安全性。
→ 通俗说:“它是个银行账户,要能存钱、能查余额,还得防着多人同时瞎改余额”。 -
AddMoneyThread
类的核心职责:
作为线程,执行“给指定账户存指定金额”的任务。
→ 通俗说:“它是个办事员,要知道去哪个银行账户(Account
)存多少钱(金额)”。
第二步:从职责倒推每个类需要的参数(再想“需要什么才能完成职责”)
参数的本质是“类完成职责所必需的外部信息”,缺了这些信息,类就无法工作。
1. Account
类需要哪些参数?
它的职责是“管理余额+保证安全”,需要的信息:
- 初始余额:账户刚创建时,余额是多少?(比如默认0元,但也可以让用户指定)。
→ 所以__init__
方法可以加一个可选参数initial_balance
,默认值0。 - 线程锁:为了多线程安全,需要内置一个锁(这个是类内部自己创建的,不需要外部传入,所以不算参数)。
因此 Account
类的参数设计:
class Account:def __init__(self, initial_balance=0): # 可选参数:初始余额,默认0self._balance = initial_balance # 用初始余额初始化self._lock = threading.Lock() # 内部创建锁,不用外部传
它的方法(deposit
)需要什么参数?
deposit
的职责是“存多少钱”,所以必须知道“存入金额”:
def deposit(self, money): # 必需参数:要存入的金额self._lock.acquire()try:self._balance += moneyfinally:self._lock.release()
2. AddMoneyThread
类需要哪些参数?
它的职责是“作为线程给指定账户存指定金额”,需要的信息:
- 要操作的账户:必须知道是给哪个
Account
实例存钱(没有账户,存哪里?)。 - 要存的金额:必须知道每次存多少钱(没有金额,存多少?)。
这两个信息都需要外部提供(线程自己无法生成),所以必须作为参数传入 __init__
方法。
因此 AddMoneyThread
类的参数设计:
class AddMoneyThread(Thread):def __init__(self, account, money): # 两个必需参数:账户实例、存入金额super().__init__() # 必须调用父类Thread的初始化self._account = account # 保存账户实例(后面要用它调用deposit)self._money = money # 保存存入金额(后面要传给deposit)
它的 run
方法需要参数吗?
不需要。因为 run
是线程启动后自动执行的,它的逻辑是固定的:“用保存的账户和金额,调用 deposit
方法”:
def run(self):self._account.deposit(self._money) # 直接用实例属性,不用额外传参
第三步:主函数如何调用?(把类串联起来)
主函数的任务是“创建100个线程给同一个账户各存1元”,步骤:
- 创建一个
Account
实例(用默认初始余额0)。 - 循环100次,每次创建
AddMoneyThread
实例时,传入:- 同一个
Account
实例(所有线程操作同一个账户); - 金额1(每次存1元)。
- 同一个
- 启动所有线程,等待全部完成后,打印余额。
完整代码:
关键规律:参数设计的“必要性原则”
判断一个参数是否需要存在,就问自己:
“没有这个参数,这个类还能完成它的核心职责吗?”
Account
类没有initial_balance
可以(用默认0),但deposit
没有money
就不行(不知道存多少)。AddMoneyThread
类没有account
不行(不知道存到哪),没有money
也不行(不知道存多少)。
按照这个原则,你就能清晰地确定每个类需要哪些参数了。刚开始写不出来很正常,多按照“职责→参数→方法”的逻辑链练习几次,很快就能掌握这种设计思路。