使用 Super 构建适当的 Python 外观 Class?
Constructing Proper Python Facade Class with Super?
我以为我掌握了 Super() 的多重继承,并试图在 Facade class 中使用它,但我 运行 遇到了一个奇怪的错误。我正在使用制作精良的 Python 工作流软件,称为 Fireworks,但是 classes 和工作流任务的结构非常严格,因此我创建了一个 Facade class 以便于使用。
下面是工作流任务的基本结构:
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:
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
的新实例时,它不会创建空实例。在先前实例中设置的输入集参数以某种方式被转移到新实例中,但不是 kpoints
或 qshift
。例如:
>>> 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 class 中的 self.__dict__['isp'] = isp
更改为 self.__dict__['isp'] = isp if isp else {}
,一切似乎都按预期工作。在先前实例中设置的参数不会转移到新实例中。那么,为什么 Facade class 不默认为 isp={}(假设这是 __ init __ 中的默认值),如果没有给定输入集参数,它应该如此创建时?它从哪里提取先前的参数,因为默认值应该是一个空白字典?
需要说明的是,我有一个解决方案可以使 Facade Class 发挥我预期的功能(通过将 Facade 中的 isp
更改为 self.__dict__['isp'] = isp if isp else {}
),但我想弄清楚为什么需要这样做。我相信我遗漏了 super
或 Python 中的方法解析顺序的一些基本内容。我很好奇为什么会发生这种情况并试图扩展我的知识库。
下面是 Facade 的方法解析顺序 Class。
>>> 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)
python 中的可变默认参数无法像您期望的那样工作;您的函数定义(省略无关参数):
def __init__(self, ..., isp={}, ...):
<CODE>
相当于:
DEFAULT_ISP = {}
def __init__(self, ..., isp=DEFAULT_ISP, ...):
<CODE>
(除了 DEFAULT_ISP
变量不可用。)
上面的代码清楚地表明你的两个任务正在使用相同的 isp
字典,这显然是由属性设置器修改的。
我以为我掌握了 Super() 的多重继承,并试图在 Facade class 中使用它,但我 运行 遇到了一个奇怪的错误。我正在使用制作精良的 Python 工作流软件,称为 Fireworks,但是 classes 和工作流任务的结构非常严格,因此我创建了一个 Facade class 以便于使用。
下面是工作流任务的基本结构:
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:
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
的新实例时,它不会创建空实例。在先前实例中设置的输入集参数以某种方式被转移到新实例中,但不是 kpoints
或 qshift
。例如:
>>> 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 class 中的 self.__dict__['isp'] = isp
更改为 self.__dict__['isp'] = isp if isp else {}
,一切似乎都按预期工作。在先前实例中设置的参数不会转移到新实例中。那么,为什么 Facade class 不默认为 isp={}(假设这是 __ init __ 中的默认值),如果没有给定输入集参数,它应该如此创建时?它从哪里提取先前的参数,因为默认值应该是一个空白字典?
需要说明的是,我有一个解决方案可以使 Facade Class 发挥我预期的功能(通过将 Facade 中的 isp
更改为 self.__dict__['isp'] = isp if isp else {}
),但我想弄清楚为什么需要这样做。我相信我遗漏了 super
或 Python 中的方法解析顺序的一些基本内容。我很好奇为什么会发生这种情况并试图扩展我的知识库。
下面是 Facade 的方法解析顺序 Class。
>>> 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)
python 中的可变默认参数无法像您期望的那样工作;您的函数定义(省略无关参数):
def __init__(self, ..., isp={}, ...):
<CODE>
相当于:
DEFAULT_ISP = {}
def __init__(self, ..., isp=DEFAULT_ISP, ...):
<CODE>
(除了 DEFAULT_ISP
变量不可用。)
上面的代码清楚地表明你的两个任务正在使用相同的 isp
字典,这显然是由属性设置器修改的。