2016-05-31 6 views
3

私はSuper()で多重継承のハンドルを持っていて、それをFacadeクラスの中で使用しようとしていたと思っていましたが、私は奇妙なバグに遭遇しています。私はFireworksと呼ばれるよく作られたPython Workflowソフトウェアを使用していますが、クラスとワークフロータスクの構造はかなり堅いので、使いやすさのために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という新しいインスタンスを作成する場合、空のインスタンスは作成されません。以前のインスタンスで設定された入力セットパラメータは、新しいインスタンスに引き継がれますが、kpointsまたはqshiftではありません。例えば:私はself.__dict__['isp'] = isp if isp else {}すべてに私のファサードクラスでself.__dict__['isp'] = ispを変更した場合

>>> 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クラスがisp = {}にデフォルト設定されていないのはなぜですか(これは__ init __のデフォルトです)。デフォルトは空の辞書でなければならないので、どこから前のパラメータを引っ張っていますか?

ファサードクラスの機能を、ファサードクラスの機能をself.__dict__['isp'] = isp if isp else {}に変更することで、期待通りに解決策が得られましたが、これがなぜ必要なのかを判断しようとしています。私はsuperの基本的なものや、Pythonのメソッド解決の順序が不足していると思います。私はなぜこれが起きているのか、私の知識ベースを広げようとしているのが不思議です。

以下はファサードクラスのメソッド解決順序です。

>>> 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__'に[変更可能なデフォルト引数](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変数が利用できないことを除いて。)

上記のコードは明らかにすることを示していますあなたの2つのタスクは、同一のisp dictoinaryを使用しています。これは明らかに属性設定ツールによって変更されています。

関連する問題