如何在 Python class 中更好地隔离我的数据

How to segregate my data better within a Python class

我一直在做一个电子零件计算器的项目。这是我的第一个全面 Python 项目,一路上经历了很多次迭代。

数据没有像我想要的那样组织得很好,我认为这使得改进程序变得困难。

我有一个 class 模块,里面有两个部分。每个部分都有自己的相互独立的属性,整个模块本身有一些部分不关心的属性,一些部分需要的属性。该模块还必须根据部件内部进行的独立计算的结果进行一些计算。 Here is a photo of the idea I'm trying to explain. 顺便说一句,有时有些模块只有一个部分,但我可以通过应用一些 0 的数组来抑制另一部分。我希望能够有一个模块,其中一个部分完全缺失,但这不是我当前的目标。

问题是我的 class 开头有 ~100 行 self.XXX = None 来初始化所有内容,以及 IMO 重复的几个函数。单步执行代码时遍历数据也相当困难——例如,我必须找到 self.current__partA 和 self.current__partB 这样的变量。我认为有帮助的是 self.partA.current 之类的东西。如果这样写的话,我觉得可读性会更好。

问题是,我试过 subclasses 似乎我无法实现这种想法,因为当我初始化 subclass 时,我必须初始化一个新的 super class,意思是有两个superclasses(两个模块共4个部分,当我想要1个module/2部分),所以我无法真正访问信息两个 subclass 都来自 superclass 因为每个 subclass 都有自己的 superclass.

实例

我也查看了内部 classes 但有一个问题,我认为我无法真正从内部 class 访问外部 class,哪种违背了使用它的目的。这在一定程度上可行,但据我所知,这会使我的代码更长且可读性更差。

我的第一个解决方案是字典之类的东西,我并不完全讨厌它,但这会导致非常糟糕的代码,对错误的容忍度非常低。原因是,当你向字典中添加一个列表时,你不能有一个自动抛出错误的函数。我可以查字典,但就是感觉不自然。在我看来,将每个值保留为 class 变量并使用函数、getter 和 setter 通过计算来操纵它会更有意义。

我的主要目标是有效地组织数据和代码,这样我就可以使用更少的代码行,程序更容易修改,也更容易逐步完成整个过程。我并不完全相信 class 结构,它似乎是适应我正在尝试做的事情的最佳方式。有没有一种方法可以实现我在这里的要求,或者是否有一种通常更 pythonic 的方式来组织我的代码,从而产生更有效的解决方案?

class Module:
def __init__(self, module_file):
    temp_ic, temp_value = self.get__corrected_value(module_file)
    temp_if, temp_vf = self.get__corrected_value(module_file)
    self.ic_value = interp1d(temp_ic, temp_value, fill_value='extrapolate')
    self.ic_esw_on = interp1d(self.get__corrected_esw(module_file), self.get__corrected_esw(module_file["ESWON - IC ESWON"]), fill_value='extrapolate')
    self.ic_esw_off = interp1d(self.get__corrected_esw(module_file["IC - IC ESWOFF"]), self.get__corrected_esw(module_file["ESWOFF - IC ESWOFF"]), fill_value='extrapolate')
    self.rg_on_esw_on = interp1d(module_file["RGON - ESWON RGON"], module_file["ESWON - ESWON RGON"], fill_value='extrapolate')
    self.rg_off_esw_off = interp1d(module_file["RGOFF - ESWOFF RGOFF"], module_file["ESWOFF - ESWOFF RGOFF"], fill_value='extrapolate')
    self.ic_err = interp1d(self.get__corrected_esw(module_file["IC - IC ERR"]), self.get__corrected_esw(module_file["ERR - IC ERR"]), fill_value='extrapolate')
    self.if_vf = interp1d(temp_if, temp_vf, fill_value='extrapolate')
    self.rg_on_err = interp1d(module_file["RGON - ERR RGON"], module_file["ERR - ERR RGON"], fill_value='extrapolate')
    self.nameplate_vcc = module_file['Nameplate VCC']
    if module_file['vcc_ratio'] > 0:
        self.vcc_ratio = module_file['vcc_ratio']
    else:
        self.vcc_ratio = 0
    self.name = self.get__module_name(module_file)
    self.current__PartA = []
    self.current__PartB = []
    self.some_thing_loss__PartA = []
    self.esw_on_loss = []
    self.esw_off_loss = []
    self.esw_loss__PartA = []
    self.energy__PartA = []
    self.value__PartA = []
    self.some_thing_loss__PartB = []
    self.err_loss = []
    self.energy__PartB = []
    self.value__PartB = []
    self.rg_scalar_esw_on = None
    self.rg_scalar_esw_off = None
    self.rg_scalar_err = None
    self.value_dc__PartA = module_file['PartA value DC']
    self.value_dc__PartB = module_file['PartB value DC']
    self.value_dc__module = module_file['Module value DC']
    self.trans_r_values__PartA = module_file["PartA R Values"]
    self.trans_t_values__PartA = module_file["PartA T Values"]
    self.trans_r_values__PartB = module_file["PartB R Values"]
    self.trans_t_values__PartB = module_file["PartB T Values"]
    self.some_thing_loss_total__PartA = None
    self.some_thing_loss_total__PartB = None
    self.esw_on_loss_total = None
    self.esw_off_loss_total = None
    self.esw_loss_total = None
    self.err_loss_total = None
    self.device_loss_total__PartA = None
    self.device_loss_total__PartB = None
    self.module_loss_total = None
    self.delta_tcase_ave = None
    self.delta_value_ave__PartA = None
    self.delta_value_ave__PartB = None
    self.nominal_value_ave__PartA = None
    self.nominal_value_ave__PartB = None
    self.delta_value_max__PartA = None
    self.delta_value_max__PartB = None
    self.nominal_value_max__PartA = None
    self.nominal_value_max__PartB = None
    self.value_max_PartA_list = []
    self.value_max_PartB_list = []
    self.thermal_interp_is_four_degree = self.check__thermal_interp()
    self.switches_per_degree = None
    self.input_output_freq = None
    self.time_division = None
    self.input_t_sink = None
    self.step_size = None
    self.step_range = None
    self.sec_per_cycle_degree = None
    self.duty_p = None
    self.value_PartA_list = None
    self.value_PartB_list = None
    self.time_list = None
    self.rad_list = None
    self.value_max__PartA_thermo = None
    self.value_max__PartB_thermo = None
    self.value_max__time_value = None

def check__some_input_conditions_and_change_input(self):  # todo could this be cleaned?
    blah

def get__max_current(self):
    return max(self.nominal_value_max__PartB, self.nominal_value_max__PartA)

def set__some_module_values(self, is_three_level, system):  # todo call this something different, and break it out for 3-level
    blah

def set_values_for_both_parts(self, input_instance, system_instance, module_location=None):
    lots of blah

def set__current_PartA(self, current):
    self.current__PartA = current

def set__current_partB(self, current):
    blah

def calculate__another_other_loss_for_part_A(self, duty):
    blah

def calculate__another_loss_for_partB(self, duty):
    blah

def calculate__another_loss_for_partA(self, duty=None):
    blah

def calculate__some_loss_for_partA(self, duty=None):
    blah

def calculate__some_loss_for_partB(self, duty=None):
    blah

def calculate__energy_power_for_both_parts(self):
    blah

def calculate__temperatures_for_both_parts(self):
    blah

def calculate__max_temp(self):  # maybe split into PartA and PartB separately?
    self.create_thermal_resistance_dict()
    value_PartA_list = []
    value_PartB_list = []

    next_array_PartA = self.value__PartA
    next_array_PartA = self.rotate(next_array_PartA, -1)
    delta_p_PartA = [next_el - last_el for next_el, last_el in zip(next_array_PartA, self.value__PartA)]
    last_power_PartA = self.value__PartA[-1] - self.device_loss_total__PartA
    first_power_PartA = self.value__PartA[0] - self.device_loss_total__PartA
    value_dict_PartA_added = [self.get_PartA_value_from_time(i * self.sec_per_cycle_degree + self.value_max__time_value) for i in range(self.step_range)]
    value_dict_PartA_added = [old + new for old, new in zip(self.value_max__PartA_thermo, value_dict_PartA_added)]
    value_PartA_inst_init = [self.input_t_sink + self.delta_value_ave__PartA + self.delta_tcase_ave - last_power_PartA * self.value_max__PartA_thermo[i] + first_power_PartA * value_dict_PartA_added[i] for i in range(self.step_range)]

    delta_value_PartB = self.device_loss_total__PartB * self.value_dc__PartB
    next_array_PartB = self.value__PartB
    next_array_PartB = self.rotate(next_array_PartB, -1)
    delta_p_PartB = [next_el - last_el for next_el, last_el in zip(next_array_PartB, self.value__PartB)]
    last_power_PartB = self.value__PartB[-1] - self.device_loss_total__PartB
    first_power_PartB = self.value__PartB[0] - self.device_loss_total__PartB
    value_dict_PartB_added = [self.get_PartB_value_from_time(i * self.sec_per_cycle_degree + self.value_max__time_value) for i in range(self.step_range)]
    value_dict_PartB_added = [old + new for old, new in zip(self.value_max__PartB_thermo, value_dict_PartB_added)]
    value_PartB_inst_init = [self.input_t_sink + delta_value_PartB + self.delta_tcase_ave - last_power_PartB * self.value_max__PartB_thermo[i] + first_power_PartB * value_dict_PartB_added[i] for i in range(self.step_range)]

    for index in range(self.step_range):
        value_dict_PartA_fix = [value_dict_PartA_added[i] if i <= index else self.value_max__PartA_thermo[i] for i in range(self.step_range)]
        # value_dict_PartA_fix_orig = [val for val in value_dict_PartA_fix]
        value_dict_PartA_fix.reverse()

        new_value_PartA = self.rotate(value_dict_PartA_fix, index)
        new_value_PartA = new_value_PartA[:359]
        temp_add_vals_PartA = [delta_p * value for delta_p, value in zip(delta_p_PartA, new_value_PartA)]
        sum_temp_add_vals_PartA = sum(temp_add_vals_PartA)
        value_PartA_list.append(sum_temp_add_vals_PartA)

        value_dict_PartB_fix = [value_dict_PartB_added[i] if i <= index else self.value_max__PartB_thermo[i] for i in range(self.step_range)]
        # value_dict_PartB_fix_orig = [val for val in value_dict_PartB_fix]
        value_dict_PartB_fix.reverse()

        new_value_PartB = self.rotate(value_dict_PartB_fix, index)
        new_value_PartB = new_value_PartB[:359]
        temp_add_vals_PartB = [delta_p * value for delta_p, value in zip(delta_p_PartB, new_value_PartB)]
        sum_temp_add_vals_PartB = sum(temp_add_vals_PartB)
        value_PartB_list.append(sum_temp_add_vals_PartB)

    value_PartA_list = [value + diff for value, diff in zip(value_PartA_inst_init, value_PartA_list)]
    value_ave_PartA = self.nominal_value_ave__PartA - np.average(value_PartA_list)
    self.value_PartA_list = [value + value_ave_PartA for value in value_PartA_list]

    value_PartB_list = [value + diff for value, diff in zip(value_PartB_inst_init, value_PartB_list)]
    value_ave_PartB = self.nominal_value_ave__PartB - np.average(value_PartB_list)
    self.value_PartB_list = [value + value_ave_PartB for value in value_PartB_list]

    self.time_list = [i * self.sec_per_cycle_degree + self.value_max__time_value for i in range(self.step_range)]
    self.rad_list = [i * self.step_size for i in range(self.step_range)]

    self.nominal_value_max__PartA = max(value_PartA_list)
    self.nominal_value_max__PartB = max(value_PartB_list)
    self.delta_value_max__PartA = max(self.value_PartA_list) - self.input_t_sink
    self.delta_value_max__PartB = max(self.value_PartB_list) - self.input_t_sink
    self.value_max_PartA_list = value_PartA_list
    self.value_max_PartB_list = value_PartB_list

def rotate(self, l, n):
    return l[-n:] + l[:-n]

def do_calculation_for_either_part(self, step, spcd, index, scalar, growth, time):  # todo does everything need to be passed in?
    blah

def get_other_part's_value(self, time):  # todo could this be folded into below
    blah

def get_one_part's_value(self, time):
    blah

def integrate_value_for_other_part(self, step, spcd, start_time, index):  # todo could this be folded into below
    blah

def integrate_value_for_one_part(self, step, spcd, start_time, index):  # todo remove interp check
    blah

def create_some_dict_for_both_parts(self):  # todo could this be cleaned
    50 lines of blah

def get__other_corrected_array(self, array):  # todo could this be simplified?
    blah

def get__corrected_array(self, input arrays):  # todo is this necessary
    blah

def get__some_value(self, value):  # todo isn't there one of these already?
    blah

def get__module_name(self, module_file):
    blah

评论员是正确的,MCVE 肯定会增强您的 post,因此我的回答有点有限。我只想指出,您的数据成员可以是任何 python 对象。

因此,如果您的数据访问模式受益于将数据存储在 pandas 中并作为 pandas 与其交互:

class YourClass:
    def __init__(self, data):
        self.data = # Your pandas df

或json:

import json
class YourClass:
    def __init__(self, data):
        self.data = json.loads(data)

或 numpy:

class YourClass:
    def __init__(self, data):
        self.data = # Your numpy ndarray

然后你的 class 可以简单地称为 YourClass(data)

编辑:看看你的代码,在我看来你所有的 self.value = None 行都是多余的。如果它们是表格数据输入的成员,则可以对其进行初始化:

class Module:
    def __init__(self, data):
        self.data = pd.DataFrame()

一旦将它们初始化为空数据框,它们的 CRUD 操作就可以映射到非常成熟的 pandas CRUD 操作。类似地,self.data = {} 用于键值对数据结构,如 JSON,等等。对于其余部分,您可以捕获 data.key 在通用 getter 和 setter 中未定义的情况,而不必为初始化它们而烦恼。