如何以更Pythonic的方式判断Basket实例是否完成初始化?
更Pythonic的实例初始化判断方案
业务背景与需求
- 当
total属性的组成部分(由类属性_fruits定义)发生变化时,需实时更新total值 - 实际场景约束:
total计算成本高、被频繁读取,但组成部分很少变化,因此必须控制计算时机以优化性能
现有问题
当前实现通过_initialised标志位避免初始化阶段设置属性时触发compute_total(此时部分属性未定义会引发AttributeError),但手动管理标志位不够优雅,希望找到更符合Python风格的替代方案。
现有实现代码
from abc import ABC, abstractmethod class Container(ABC): _fruits = [] def __init__(self): self._initialised = False def __setattr__(self, name, value): super().__setattr__(name, value) if name in self._fruits and hasattr(self, 'compute_total') and self._initialised: self.compute_total() @abstractmethod def compute_total(self): pass class Basket(Container): _fruits = ['apple', 'orange'] def __init__(self, apple, orange, color): super(Basket, self).__init__() self.apple = apple self.orange = orange self.color = color self._total = 0 self.compute_total() self._initialised = True @property def total(self): return self._total def compute_total(self): self._total = self.apple + self.orange
优化方案
方案1:检查所有必要属性是否已初始化
直接在__setattr__中判断_fruits定义的所有属性是否都已存在于实例中,只有当全部属性都设置完成后才触发计算,无需额外标志位:
from abc import ABC, abstractmethod class Container(ABC): _fruits = [] def __setattr__(self, name, value): super().__setattr__(name, value) if name in self._fruits and hasattr(self, 'compute_total'): # 验证所有_fruits属性都已设置完成 all_fruits_ready = all(hasattr(self, fruit) for fruit in self._fruits) if all_fruits_ready: self.compute_total() @abstractmethod def compute_total(self): pass class Basket(Container): _fruits = ['apple', 'orange'] def __init__(self, apple, orange, color): super().__init__() self.apple = apple self.orange = orange self.color = color self._total = 0 # 无需手动调用compute_total,设置最后一个水果属性时会自动触发 @property def total(self): return self._total def compute_total(self): self._total = self.apple + self.orange
优点:逻辑直观,完全去掉标志位管理,符合Python的"显式优于隐式"原则。
方案2:使用上下文管理器标记初始化阶段
通过上下文管理器封装初始化流程,自动控制计算触发的时机:
from abc import ABC, abstractmethod class Container(ABC): _fruits = [] def __init__(self): self._is_initialising = True def __enter__(self): self._is_initialising = True return self def __exit__(self, exc_type, exc_val, exc_tb): self._is_initialising = False self.compute_total() def __setattr__(self, name, value): super().__setattr__(name, value) if name in self._fruits and hasattr(self, 'compute_total') and not self._is_initialising: self.compute_total() @abstractmethod def compute_total(self): pass class Basket(Container): _fruits = ['apple', 'orange'] def __init__(self, apple, orange, color): super().__init__() with self: self.apple = apple self.orange = orange self.color = color self._total = 0 @property def total(self): return self._total def compute_total(self): self._total = self.apple + self.orange
优点:初始化流程边界清晰,通过with语句显式标记初始化阶段,代码可读性强。
方案3:检查调用栈判断是否处于初始化中
利用inspect模块检查当前调用栈,判断是否在执行__init__方法,从而决定是否触发计算:
import inspect from abc import ABC, abstractmethod class Container(ABC): _fruits = [] def __setattr__(self, name, value): super().__setattr__(name, value) if name in self._fruits and hasattr(self, 'compute_total'): # 检查调用栈中是否存在__init__方法 in_init = any(frame.function == '__init__' for frame in inspect.stack()) if not in_init: self.compute_total() @abstractmethod def compute_total(self): pass class Basket(Container): _fruits = ['apple', 'orange'] def __init__(self, apple, orange, color): super().__init__() self.apple = apple self.orange = orange self.color = color self._total = 0 self.compute_total() @property def total(self): return self._total def compute_total(self): self._total = self.apple + self.orange
优点:完全无需额外属性,利用Python运行时信息判断状态;缺点是inspect.stack()有轻微性能开销,适合初始化频率不高的场景。
内容的提问来源于stack exchange,提问作者Gwalchaved
相关产品推荐
相关产品推荐

