__getattribute__与__getattr__问题排查及ATM代码修复咨询
问题分析与修复方案
为什么启用__getattribute__会触发NoneType错误?
当你重写Python类的__getattribute__方法时,它会拦截所有属性/方法的访问请求——不管这个属性是否存在。如果你的实现没有返回正确的属性对象(比如忘了写return,或者错误返回None),调用atm.add_rub(50)时,add_rub会被解析成None,自然就触发TypeError: 'NoneType' object is not callable。
举个你可能写错的反例:
class ATM: def __init__(self): self.rub_balance = 0 def __getattribute__(self, name): # 只做了非法方法检查,但没返回合法方法对象 if name.startswith('add_') and name != 'add_rub': raise ValueError("仅支持卢布") def add_rub(self, amount): self.rub_balance += amount
这个__getattribute__没有返回值,默认返回None,所以atm.add_rub拿到的是None,调用必报错。
正确写法是:合法的属性访问必须调用父类的__getattribute__获取原属性/方法并返回:
class ATM: def __init__(self): self.rub_balance = 0 def __getattribute__(self, name): if name.startswith('add_') and name != 'add_rub': raise ValueError("本ATM仅支持卢布操作") # 合法请求交给父类处理,返回正确的方法对象 return super().__getattribute__(name) def add_rub(self, amount): self.rub_balance += amount
更合理的实现:用__getattr__替代__getattribute__
你的需求(拦截非法货币方法)其实更适合__getattr__——它只在访问不存在的属性/方法时触发,不会干扰add_rub这类合法方法的正常调用,代码更简洁:
class ATM: def __init__(self): self._rub_balance = 0 # 改成私有属性,避免外部直接修改 def __getattr__(self, name): if name.startswith('add_'): raise ValueError("本ATM仅支持卢布操作") # 其他不存在的属性按默认逻辑抛错 raise AttributeError(f"'ATM' object has no attribute '{name}'") def add_rub(self, amount): # 增加输入验证:金额必须为正的数字 if not isinstance(amount, (int, float)) or amount <= 0: raise ValueError("添加金额必须为正数") self._rub_balance += amount def get_rub_balance(self): return f"当前余额:{self._rub_balance} 卢布"
调用atm.add_usd(100)会触发__getattr__抛错,atm.add_rub(50)则直接执行正常逻辑,不会有任何问题。
其他可修复/优化点
- 封装核心数据:把余额设为私有属性(比如
_rub_balance),禁止外部直接修改,只能通过指定方法操作,保证数据安全。 - 自定义异常类:可以定义
UnsupportedCurrencyError替代通用的ValueError,让错误信息更精准。 - 完善功能:添加
withdraw_rub取款方法,同时做余额校验(取款金额不能超过当前余额)。 - 统一异常处理:给所有操作添加规范的异常抛出逻辑,方便调用方捕获和处理错误。
内容的提问来源于stack exchange,提问作者VinogradovIlya
相关产品推荐
相关产品推荐

