用Super构造合适的Python Facade类?

2024-10-01 09:22:08 发布

您现在位置:Python中文网/ 问答频道 /正文

我原以为我用Super()处理了多重继承,并试图在Facade类中使用它,但我遇到了一个奇怪的bug。我使用的是一个制作精良的Python工作流软件Fireworks,但是类和工作流任务的结构非常严格,因此我创建了一个Facade类以便于使用。你知道吗

工作流任务的基本结构如下:

class BgwInputTask(FireTaskBase):

    required_params = ['structure', 'pseudo_dir', 'input_set_params', 'out_file']
    optional_params = ['kpoints', 'qshift', 'mat_type']

    def __init__(self, params): 
        self.structure = Structure.from_dict(params.get('structure').as_dict())
        self.pseudo_dir = params.get('pseudo_dir')
        self.kpoints = params.get('kpoints', None)
        self.qshift = params.get('qshift', None)
        self.isp = params.get('input_set_params')
        self.run_type = params.get('run_type', None)
        self.mat_type = params.get('mat_type', 'metal')
        self.filename = params.get('out_file')

        '''
        misc code for storing pseudo_potentials in:
        self.pseudo_files
        self.occupied_bands
        etc...
        '''

        params = {'structure': self.structure, 'pseudo_dir': self.pseudo_dir,
            'kpoints': self.kpoints, 'qshift': self.qshift,
            'input_set_params': self.isp, 'run_type': self.run_type,
            'mat_type':self.mat_type, 'out_file': self.filename}
        self.update(params)

    def write_input_file(self, filename):
        <code for setting up input file format and writing to filename>

我用下面的super构建了Facade类:

class BgwInput(BgwInputTask):

    def __init__(self, structure, pseudo_dir, isp={},
                kpoints=None, qshift=None, mat_type='semiconductor',
                out_file=None):

        self.__dict__['isp'] = isp
        self.__dict__['run_type'] = out_file.split('.')[0]
        self.__dict__['params'] = {'structure': structure, 'pseudo_dir': pseudo_dir,
            'kpoints': kpoints, 'qshift': qshift,
            'input_set_params': self.isp, 'mat_type': mat_type,
            'out_file': out_file, 'run_type': self.run_type}
        print("__init__: isp: {}".format(self.isp))
        print("__init__: runtype: {}".format(self.run_type))

        super(BgwInput, self).__init__(self.params)

    def __setattr__(self, key, val):
        self.proc_key_val(key.strip(), val.strip()) if isinstance(
            val, six.string_types) else self.proc_key_val(key.strip(), val)

    def proc_key_val(self, key, val):
        <misc code for error checking of parameters being set>
        super(BgwInput, self).__dict__['params']['input_set_params'].update({key:val})

除了一个让我困惑的小警告之外,这个方法很有效。创建BgwInput的新实例时,它不会创建空实例。在以前的实例中设置的输入集参数会以某种方式传递到新实例,而不是kpointsqshift。例如:

>>> epsilon_task = BgwInput(structure, pseudo_dir='/path/to/pseudos', kpoints=[5,5,5], qshift=[0, 0, 0.001], out_file='epsilon.inp')
__init__: isp: {}
__init__: runtype: epsilon

>>> epsilon_task.epsilon_cutoff = 11.0
>>> epsilon_task.number_bands = 29


>>> sigma_task = BgwInput(structure, pseudo_dir='/path/to/pseudos', kpoints=[5,5,5], out_file='sigma.inp')
__init__: isp: {'epsilon_cutoff': 11.0, 'number_bands': 29}
__init__: runtype: sigma

但是,如果我将Facade类中的self.__dict__['isp'] = isp更改为self.__dict__['isp'] = isp if isp else {},那么一切似乎都能按预期工作。在以前的实例中设置的参数不会转移到新实例。那么,为什么Facade类没有默认为isp={}(假设这是{strong>init}中的默认值),如果在创建时没有给定输入集参数,它应该是这样的呢?因为默认值应该是一个空白字典,所以它从何处提取以前的参数?你知道吗

为了清楚起见,我已经找到了一个解决方案,使Facade类按我所期望的那样工作(通过将Facade中的isp更改为self.__dict__['isp'] = isp if isp else {}),但是我正在试图弄清楚为什么需要这样做。我相信我在Python中缺少了一些基本的super或方法解析顺序。我很好奇为什么会发生这种情况,并试图扩大我的知识基础。你知道吗

下面是Facade类的方法解析顺序。你知道吗

>>> BgwInput.__mro__

(pymatgen.io.bgw.inputs.BgwInput,
pymatgen.io.bgw.inputs.BgwInputTask,
fireworks.core.firework.FireTaskBase,
collections.defaultdict,
dict,
fireworks.utilities.fw_serializers.FWSerializable, 
object)

Tags: selfinittypedirvalparamsoutfacade
1条回答
网友
1楼 · 发布于 2024-10-01 09:22:08

python中可变的默认参数并不像您期望的那样工作;您的函数定义(忽略了不相关的参数):

def __init__(self, ..., isp={}, ...):
   <CODE>

相当于:

DEFAULT_ISP = {}
def __init__(self, ..., isp=DEFAULT_ISP, ...):
   <CODE>

(除了DEFAULT_ISP变量不可用。)

上面的代码清楚地显示了您的两个任务使用的是相同的ispdictoniary,这显然是由属性设置器修改的。你知道吗

相关问题 更多 >