向列表追加XLField对象仅保留最后一个的问题求助
问题:实例化对象后列表中仅最后一个对象重复出现
尝试将DataFrame的每行数据实例化为XLField类对象,追加到列表后,列表中所有元素都是最后一个对象的重复值。在追加前打印对象属性能得到预期不同结果,但遍历列表时全是重复的最后一个值。
核心代码
import pandas as pd df_dict = pd.read_excel(excel_path).to_dict("records") xl_field_list = [] for field in df_dict: xl_field = XLField( field1=field["Field 1"], field2=field["Field 2"], field3=field["Field 3"], field4=field["Field 4"], ) xl_field_list.append(xl_field) for f in xl_field_list: print(f.field1)
运行结果
0052 0052 0052 0052 0052 0052
XLField类定义
import re class XLField1: def __get__(self, obj, objtype=None): return self.value def __set__(self, obj, value): value = value.upper() if not re.fullmatch(r"[A-Z0-9]{2}", value): raise ValueError("Check Field 1") self.value = value class XLField2: def __get__(self, obj, objtype=None): return self.value def __set__(self, obj, value): value = str(value).zfill(4) if not re.fullmatch(r"[0-9]{3,4}", value): raise ValueError("Check Field 2") self.value = value class XLField3: Days_Of_The_Week = [ "MONDAY", "TUESDAY", "WEDNESDAY", ] def __get__(self, obj, objtype=None): return self.value def __set__(self, obj, value): value = value.upper() if value not in XLField3.Days_Of_The_Week: raise ValueError("Check Field 3") self.value = value class XLField4: INT_OR_COM = ["A", "D"] def __get__(self, obj, objtype=None): return self.value def __set__(self, obj, value): value = value.upper() if value not in XLField4.INT_OR_COM: raise ValueError("Check Field 4") self.value = value class XLField: field1 = XLField1() field2 = XLField2() field3 = XLField3() field4 = XLField4() def __init__(self, field1, field2, field3, field4): self.field1 = field1 self.field2 = field2 self.field3 = field3 self.field4 = field4
问题原因
你实现的XLField1、XLField2等是描述符类,但这些描述符把值存在了自身实例(self.value)里,而不是存在被描述的对象(即XLField的实例)中。
XLField类里的field1、field2是类属性,所有XLField的实例共享同一个XLField1、XLField2实例。每次给self.field1赋值时,实际是修改了类属性中那个唯一的XLField1实例的value,后面的赋值会覆盖前面的,最终所有实例访问field1时,都会取到最后一次赋值的value。
解决方案
修改描述符类,把值存在被描述的对象(obj)上,而不是描述符自身:
修正后的描述符类代码
import re class XLField1: def __get__(self, obj, objtype=None): return obj._field1 def __set__(self, obj, value): value = value.upper() if not re.fullmatch(r"[A-Z0-9]{2}", value): raise ValueError("Check Field 1") obj._field1 = value class XLField2: def __get__(self, obj, objtype=None): return obj._field2 def __set__(self, obj, value): value = str(value).zfill(4) if not re.fullmatch(r"[0-9]{3,4}", value): raise ValueError("Check Field 2") obj._field2 = value class XLField3: Days_Of_The_Week = [ "MONDAY", "TUESDAY", "WEDNESDAY", ] def __get__(self, obj, objtype=None): return obj._field3 def __set__(self, obj, value): value = value.upper() if value not in XLField3.Days_Of_The_Week: raise ValueError("Check Field 3") obj._field3 = value class XLField4: INT_OR_COM = ["A", "D"] def __get__(self, obj, objtype=None): return obj._field4 def __set__(self, obj, value): value = value.upper() if value not in XLField4.INT_OR_COM: raise ValueError("Check Field 4") obj._field4 = value class XLField: field1 = XLField1() field2 = XLField2() field3 = XLField3() field4 = XLField4() def __init__(self, field1, field2, field3, field4): self.field1 = field1 self.field2 = field2 self.field3 = field3 self.field4 = field4
说明
- 每个描述符的
__set__方法把处理后的值存入obj的私有属性(比如_field1),而不是描述符自身的self.value __get__方法从obj的私有属性中取值,这样每个XLField实例都有自己独立的属性值,不会互相覆盖
这样修改后,遍历xl_field_list时就能得到每个对象的真实属性值了。
内容的提问来源于stack exchange,提问作者redracer88
相关产品推荐
相关产品推荐

