修改列对象属性后Dataframe_Builder_Update未自动更新DataFrame问题
将由DataFrame列对象组成的数组传入Dataframe_Builder_Update类后,执行[setattr(obj, 'date', '12/29/2019') for obj in dataframe_builder_obj.column_builders]可成功更新列对象的日期及相关计算,但类的result_df不会自动同步更新。只有显式调用build_dataframe()方法才能得到正确结果,希望实现无需手动调用的自动更新。
原代码如下:
import pandas as pd class Dataframe_Builder_Update(): def __init__(self, column_builders): self._column_builders = column_builders self.build_dataframe() def build_dataframe(self): self.result_df = pd.DataFrame() for column_builder in self._column_builders: if not column_builder.group: self.result_df = pd.concat([self.result_df, column_builder.calculated_output], axis=0) elif column_builder.group: self.result_df = pd.concat([self.result_df, column_builder.calculated_output], axis=1) @property def column_builders(self): return self._column_builders @column_builders.setter def column_builders(self, new_column_builders): self._column_builders = new_column_builders self.build_dataframe() # 使用示例 # dataframe_builder_obj = Dataframe_Builder_Update(my_arr) # dataframe_builder_obj.result_df # 修改属性后result_df未更新 # [setattr(obj, 'date', '12/29/2019') for obj in dataframe_builder_obj.column_builders] # dataframe_builder_obj.result_df # 显式调用后正常 # [setattr(obj, 'date', '12/29/2019') for obj in dataframe_builder_obj.column_builders] # dataframe_builder_obj.build_dataframe()
当前column_builders的setter仅在替换整个列对象数组时才会触发build_dataframe()更新result_df。但直接修改数组内单个列对象的属性时,主类无法感知到这些内部变化,因此不会自动重建DataFrame。
方案1:将result_df改为动态计算的属性
把result_df从实例变量改成@property,每次访问时自动重新构建。这种方式实现最简单,但如果build_dataframe()计算成本高,频繁访问会影响性能。
修改后的代码:
import pandas as pd class Dataframe_Builder_Update(): def __init__(self, column_builders): self._column_builders = column_builders def build_dataframe(self): result_df = pd.DataFrame() for column_builder in self._column_builders: if not column_builder.group: result_df = pd.concat([result_df, column_builder.calculated_output], axis=0) else: result_df = pd.concat([result_df, column_builder.calculated_output], axis=1) return result_df @property def column_builders(self): return self._column_builders @column_builders.setter def column_builders(self, new_column_builders): self._column_builders = new_column_builders @property def result_df(self): # 每次访问时自动构建最新的DataFrame return self.build_dataframe()
使用时,直接访问dataframe_builder_obj.result_df就能得到基于最新列对象的结果,无需手动调用build_dataframe()。
方案2:给列对象添加观察者机制
让列对象在自身属性变化时通知Dataframe_Builder_Update实例,触发更新。这种方式更高效,只有当列对象确实变化时才会重建DataFrame。
首先定义列对象的基类,添加属性变更通知:
import pandas as pd class ColumnBuilder: def __init__(self, date, group=False): self._date = date self.group = group self._observers = [] def add_observer(self, observer): self._observers.append(observer) def _notify_observers(self): for observer in self._observers: observer.update() @property def date(self): return self._date @date.setter def date(self, new_date): self._date = new_date self._notify_observers() # 属性变更时通知观察者 @property def calculated_output(self): # 示例计算逻辑,根据date生成DataFrame return pd.DataFrame({f'col_{self._date}': [1,2,3]})
然后修改Dataframe_Builder_Update类,实现观察者接口:
class Dataframe_Builder_Update(): def __init__(self, column_builders): self._column_builders = column_builders # 给每个列对象添加当前实例作为观察者 for builder in self._column_builders: builder.add_observer(self) self.build_dataframe() def update(self): # 收到列对象变更通知时,自动重建DataFrame self.build_dataframe() def build_dataframe(self): self.result_df = pd.DataFrame() for column_builder in self._column_builders: if not column_builder.group: self.result_df = pd.concat([self.result_df, column_builder.calculated_output], axis=0) else: self.result_df = pd.concat([self.result_df, column_builder.calculated_output], axis=1) @property def column_builders(self): return self._column_builders @column_builders.setter def column_builders(self, new_column_builders): # 移除旧列对象的观察者 for builder in self._column_builders: if self in builder._observers: builder._observers.remove(self) self._column_builders = new_column_builders # 添加新列对象的观察者 for builder in self._column_builders: builder.add_observer(self) self.build_dataframe()
使用时,修改列对象的date属性后,result_df会自动更新:
# 创建列对象 col1 = ColumnBuilder('12/28/2019') col2 = ColumnBuilder('12/28/2019', group=True) my_arr = [col1, col2] # 初始化构建器 dataframe_builder_obj = Dataframe_Builder_Update(my_arr) print(dataframe_builder_obj.result_df) # 修改列对象日期,result_df自动更新 col1.date = '12/29/2019' print(dataframe_builder_obj.result_df)
方案3:在主类中封装修改列属性的方法
不在外部直接修改列对象的属性,而是通过主类提供的接口来操作,这样主类可以在修改后立即触发更新。
修改后的Dataframe_Builder_Update类:
import pandas as pd class Dataframe_Builder_Update(): def __init__(self, column_builders): self._column_builders = column_builders self.build_dataframe() def build_dataframe(self): self.result_df = pd.DataFrame() for column_builder in self._column_builders: if not column_builder.group: self.result_df = pd.concat([self.result_df, column_builder.calculated_output], axis=0) else: self.result_df = pd.concat([self.result_df, column_builder.calculated_output], axis=1) @property def column_builders(self): return self._column_builders @column_builders.setter def column_builders(self, new_column_builders): self._column_builders = new_column_builders self.build_dataframe() def update_column_date(self, index, new_date): # 根据索引修改指定列对象的日期,并触发更新 if 0 <= index < len(self._column_builders): setattr(self._column_builders[index], 'date', new_date) self.build_dataframe() def update_all_columns_date(self, new_date): # 修改所有列对象的日期,并触发更新 for obj in self._column_builders: setattr(obj, 'date', new_date) self.build_dataframe()
使用时,通过主类的方法来修改日期:
# 初始化后,修改所有列的日期 dataframe_builder_obj.update_all_columns_date('12/29/2019') # 此时result_df已经自动更新 print(dataframe_builder_obj.result_df)
内容的提问来源于stack exchange,提问作者steezebutter

