2016-05-31 6 views
3

Я думал, что у меня есть дескриптор множественного наследования с Super() и пытался использовать его внутри класса Facade, но я столкнулся с странной ошибкой. Я использую хорошо сделанное программное обеспечение Python Workflow под названием Fireworks, но структуры классов и Workflow Tasks довольно жесткие, поэтому я создал класс Facade для удобства использования.Построение правильного класса фасадов Python с супер?

Ниже Основная структура рабочего процесса Задача:

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. Например:

>>> 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'] = isp в моем классе Фасад к self.__dict__['isp'] = isp if isp else {} все, кажется, работает, как ожидалось. Параметры, установленные в предыдущих экземплярах, не переносятся на новый экземпляр. Итак, почему класс Facade не работает по умолчанию isp = {} (если это значение по умолчанию в __ init __), как и должно, если не заданы параметры ввода при создании? Где он тянет предыдущие параметры с тех пор, как по умолчанию ДОЛЖЕН быть пустым словарем?

Для того, чтобы быть понятным, у меня есть решение, чтобы сделать функцию класса фасадов, как я ожидал, (поменяв isp на фасад до 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

У вас есть [изменяемый аргумент по умолчанию] (http://stackoverflow.com/questions/1132941/least-astonishment-in-python-the-mutable-default-argument) в вашем '__init__'. – user2357112

+0

Благодарим вас за эту информацию. Я не знал о изменяемых аргументах по умолчанию. Это определенно объясняет поведение, которое я видел. Таким образом, это не был «супер» или «порядок разрешения метода», о котором мне не хватало, это было частью основных аргументов Python, которые я отсутствовал. –

ответ

1

Переменные аргументы по умолчанию в python не работают так, как вы ожидаете от них; Ваше определение функции (не относящиеся к делу аргументы опущены):

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

равносильна этому:

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

(. За исключением того, что переменная DEFAULT_ISP не доступна)

Приведенный выше код ясно показывает, что ваши две задачи используют одну и ту же директиву isp, которая, по-видимому, модифицируется установщиками атрибутов.