2016-05-31 50 views
3

我以爲我有一個與超級()的多重繼承的句柄,並試圖在Facade類中使用它,但我遇到了一個奇怪的錯誤。我使用了一個名爲Fireworks的製作良好的Python工作流程軟件,但類和工作流任務的結構非常僵硬,所以我創建了Facade類以便於使用。用Super創建適當的Python 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

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 

但是,如果我在門面類改變self.__dict__['isp'] = ispself.__dict__['isp'] = isp if isp else {}似乎一切都按預期運行。在先前實例中設置的參數不會轉移到新實例。那麼,爲什麼Facade類默認不是isp = {}(因爲這是__ init __中的默認值),因爲它應該在創建時未提供輸入集參數?自從默認的應該是一個空白字典以來,它從哪裏拉動了以前的參數?

爲了清楚起見,我已經有了一個解決方案來使門面類功能正如我預期的那樣(通過將門面中的isp更改爲self.__dict__['isp'] = isp if isp else {}),但我試圖找出爲什麼需要這樣做。我相信我錯過了super或Python中的方法解析順序。我很好奇爲什麼會發生這種情況,並試圖擴展我的知識基礎。

以下是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) 
+1

你在'__init__'中有一個[mutable default argument](http://stackoverflow.com/questions/1132941/least-astonishment-in-python-the-mutable-default-argument)。 – user2357112

+0

感謝您提供此信息。我不知道可變的默認參數。這絕對解釋了我所看到的行爲。所以,它不是'super'或'method resolution order'我缺乏知識,它是我錯過的核心Python參數的一部分。 –

回答

1

Python中的可變默認參數並不像你期望的那樣工作;你的函數定義(不相關的參數被忽略):

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

等效於此:

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

(除了DEFAULT_ISP變量不可用)

上面的代碼清楚地表明,你的兩個任務使用相同的isp dictoinary,這顯然是由屬性設置者修改的。