You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python OOP实现银行系统时所有账户交易记录同步复制问题

问题现象

基于面向对象思想编写的Python银行管理系统,账户创建功能可正常运行,但执行存取款操作时存在数据串扰问题:对任意单个账户发起的交易记录,会同步出现在所有已创建账户的交易流水列表中。例如创建3个账户后仅向其中1个账户存入2000卢比,该笔存款记录会同时显示在另外2个账户的流水中,耗时5天排查未定位问题原因。

原始实现代码如下:

import datetime

try:
    
  class Bank:

      bank = dict()
      bank_preset = 1000

      def create_acc(self):
        return self.bank_preset + len(self.bank) + 1


      def set_details(self,name,pin,mobile="None",transactions=list()):
        data_dict = {
            "name":name,
            "pin":pin,
            "balance":0,
            "transactions":transactions}
            
        return data_dict


      def get_details(self,acc_no,pin):
        
        for k,v in self.bank[acc_no].items():
          print(k," -> ",v)

      
      def transaction_preset(self,tr_type, amount, date_time):
        tr_preset = {
            'tr_type': tr_type,
            'datetime': date_time,
            'amount': amount,
        }

        return tr_preset


      def deposite(self,acc_no,amount):
        
        data = self.transaction_preset('cr',amount,datetime.datetime.now())
        self.bank[acc_no]["transactions"].append(data)
        self.bank[acc_no]["balance"] += amount
          
      def withdraw(self,acc_no,amount):

        date = datetime.datetime.now()
        assert amount < self.bank[acc_no]["balance"],"Insufficient balance."
        data = self.transaction_preset('db',amount,datetime.datetime.now())


        self.bank[acc_no]["transactions"].append(data)
        self.bank[acc_no]["balance"] -= amount

      def mini_statement(self,acc_no):
        print(" All transactions ".center(60, "-"), end="\n\n")
        print("".center(50, "_"))
        print("amount".center(15) + "cr/db".center(15) + "date & time".center(15))
        print("".center(50, "_"))

        for tr_item in selfenter code here.bank[acc_no]['transactions']:
            for val in tr_item.values():
                print(str(val).center(15), end="")
            print("")


except Exception as err:
  print(err)

o1 = Bank()
acc_no = o1.create_acc()
acc_data = o1.set_details("Kuldeep",2020)
o1.bank.setdefault(acc_no,acc_data)
o1.deposite(1001,10500)
o1.bank

o2 = Bank()
acc_no = o2.create_acc()
acc_data = o2.set_details("jigar",1010)
o2.bank
根因定位

问题由两处Python语法特性的误用共同导致:

  • 可变默认参数陷阱:set_details方法中使用transactions=list()作为参数默认值。Python函数的默认参数在函数定义阶段完成初始化,而非每次调用函数时重新生成,所有未显式传入transactions参数的账户,都会共用同一个列表对象。此时向任意账户的交易列表追加数据,本质是修改这个全局共享的列表,所有引用该列表的账户自然都会看到这条交易记录。
  • 类属性共享问题:将存储所有账户数据的bank字典定义为类属性,所有Bank类的实例(如代码中的o1、o2)都会共享同一个类级别的bank字典,而非每个实例独立持有账户集合,进一步放大了数据串扰的问题。
  • 附加代码错误:mini_statement方法中存在残留输入selfenter code here.bank,属于拼写错误,运行时会直接抛出属性不存在的异常。
修复方案

按以下三点调整代码即可解决问题:

  1. 新增__init__构造方法,将bank字典、bank_preset等属性从类属性改为实例属性,保证每个Bank实例独立持有自己的账户数据集
  2. 移除set_details方法中的可变默认参数,将交易列表的初始化放到方法内部,每次创建新账户时生成独立的空交易列表
  3. 修正mini_statement方法中的拼写错误

修复后的可运行代码:

import datetime

class Bank:
    def __init__(self):
        # 实例属性,每个实例独立持有账户数据
        self.bank = dict()
        self.bank_preset = 1000

    def create_acc(self):
        return self.bank_preset + len(self.bank) + 1

    def set_details(self, name, pin, mobile="None"):
        data_dict = {
            "name": name,
            "pin": pin,
            "mobile": mobile,
            "balance": 0,
            # 每次调用生成新的独立交易列表,避免共享
            "transactions": list()
        }
        return data_dict

    def get_details(self, acc_no, pin):
        # 补充PIN校验逻辑
        if self.bank[acc_no]["pin"] != pin:
            print("PIN verification failed")
            return
        for k, v in self.bank[acc_no].items():
            print(k, " -> ", v)

    def transaction_preset(self, tr_type, amount, date_time):
        tr_preset = {
            'tr_type': tr_type,
            'datetime': date_time,
            'amount': amount,
        }
        return tr_preset

    def deposite(self, acc_no, amount):
        data = self.transaction_preset('cr', amount, datetime.datetime.now())
        self.bank[acc_no]["transactions"].append(data)
        self.bank[acc_no]["balance"] += amount
          
    def withdraw(self, acc_no, amount):
        if amount > self.bank[acc_no]["balance"]:
            raise ValueError("Insufficient balance.")
        data = self.transaction_preset('db', amount, datetime.datetime.now())
        self.bank[acc_no]["transactions"].append(data)
        self.bank[acc_no]["balance"] -= amount

    def mini_statement(self, acc_no):
        print(" All transactions ".center(60, "-"), end="\n\n")
        print("".center(50, "_"))
        print("amount".center(15) + "cr/db".center(15) + "date & time".center(15))
        print("".center(50, "_"))
        # 修正拼写错误
        for tr_item in self.bank[acc_no]['transactions']:
            for val in tr_item.values():
                print(str(val).center(15), end="")
            print("")

# 测试逻辑
if __name__ == "__main__":
    o1 = Bank()
    acc_no1 = o1.create_acc()
    acc_data1 = o1.set_details("Kuldeep", 2020)
    o1.bank.setdefault(acc_no1, acc_data1)
    o1.deposite(1001, 10500)
    
    o2 = Bank()
    acc_no2 = o2.create_acc()
    acc_data2 = o2.set_details("jigar", 1010)
    o2.bank.setdefault(acc_no2, acc_data2)
    # 此时o1的账户流水仅自己可见,o2的交易列表为空,不会出现串扰
    o1.mini_statement(1001)
    o2.mini_statement(1001)

注:Python中禁止使用列表、字典这类可变对象作为函数默认参数,否则极易出现本次遇到的数据共享问题,默认参数请尽量使用整数、字符串、None这类不可变值,需要默认空列表/字典时请在函数内部初始化。

内容的提问来源于stack exchange,提问作者Mevada Himal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 12:06:19