2013-10-10 2 views
0

Я пишу класс с несколькими конструкторами, используя @classmethod. Теперь я хотел бы, чтобы и конструктор __init__, и конструктор classmethod вызывали некоторую процедуру класса для установки начальных значений перед тем, как делать другие вещи.Mutliple конструкторы в python, вызывающие ту же самую процедуру

От __init__ это обычно делается с самим собой:

def __init__(self, name="", revision=None): 
    self._init_attributes() 

def _init_attributes(self): 
    self.test = "hello" 

Из конструктора classmethod, я бы назвал еще classmethod вместо этого, так как экземпляр (т.е. self) не создается, пока я не оставляю Метод класса с return cls(...). Теперь, я могу назвать мой _init_attributes() метод как

@classmethod 
def from_file(cls, filename=None) 
    cls._init_attributes() 
    # do other stuff like reading from file 
    return cls() 

и это на самом деле работает (в том смысле, что я не получаю сообщение об ошибке, и я могу реально увидеть атрибут тест после выполнения c = Class.from_file(). Тем не менее, если я понимаю вещи правильно, тогда это будет устанавливать атрибуты на уровне класса, а не на уровне экземпляра. Следовательно, если я инициализирую атрибут с изменяемым объектом (например, список), тогда все экземпляры этого класса будут использовать один и тот же список, а не их собственный список экземпляров. Правильно ли это? Если да, существует ли способ инициализировать атрибуты «instance» в методах класса или мне нужно написать код таким образом, чтобы вся инициализация атрибута выполнялась в init?

Хммм. Собственно, при написании этого: у меня может быть даже больше проблем, чем я думал, потому что init будет вызван по возвращении из класса, не так ли? Итак, каков был бы правильный способ справиться с этой ситуацией?

Примечание: Статья [1] обсуждает несколько схожую проблему.

+0

Вместо этого вы не могли бы просто создать '_init_attributes()' a '@ staticmethod' и явно передать ему объект, чьи атрибуты необходимо инициализировать? – martineau

ответ

1

Да, вы правильно поняли вещи: cls._init_attributes() задает атрибуты класса, а не атрибуты экземпляра.

Между тем, ваш конструктор альтернатив должен построить и вернуть экземпляр. Между конструкцией и возвратом, это когда вы можете позвонить _init_attributes(). Другими словами:

@classmethod 
def from_file(cls, filename=None) 
    obj = cls() 
    obj._init_attributes() 
    # do other stuff like reading from file 
    return obj 

Однако, вы правы, что единственный очевидный способ построения и возвращает экземпляр, это просто позвонить cls(), который будет вызывать __init__.


Но это легко обойти: просто альтернативные Конструкторы передать некоторые дополнительные аргументы в __init__ означает «пропустить обычную инициализацию, я собираюсь сделать это позже». Например:

def __init__(self, name="", revision=None, _skip_default_init=False): 
    # blah blah 

@classmethod 
def from_file(cls, filename=""): 
    # blah blah setup 
    obj = cls(_skip_default_init=True) 
    # extra initialization work 
    return obj 

Если вы хотите сделать это менее заметным, вы всегда можете взять **kwargs и проверить его внутри тела метода ... но помните, что это Python; вы не можете помешать людям делать глупые вещи, все, что вы можете сделать, это сделать очевидным, что они глупы. И _skip_default_init должно быть более чем достаточно, чтобы справиться с этим.


Если вы действительно хотите, вы можете переопределить __new__, а также.Построение объекта не вызывает __init__, если __new__ не возвращает экземпляр cls или какой-либо его подкласс. Итак, вы можете дать __new__ флаг, который сообщает ему пропустить __init__ по munging obj.__class__, а затем восстановить __class__. Это действительно взломано, но может быть полезно.


Гораздо чище решение, но по какой-то причине еще реже в Python-это заимствовать идею «класс кластера» из Smalltalk/ObjC: Создать частный подкласс, который имеет другой __init__, которая не super (или намеренно пропускает свою непосредственную базу и super s оттуда), а затем ваш альтернативный конструктор в базовом классе просто возвращает экземпляр этого подкласса.


В качестве альтернативы, если единственная причина, вы не хотите, чтобы позвонить __init__ так вы можете сделать то же самое __init__ сделали бы ... почему? DRY означает «не повторяйте себя», а не «наклоняйтесь назад, чтобы найти способы заставить себя повторить себя», правильно?

+0

Большое спасибо за этот очень ясный и полезный ответ! Именно то, на что я надеялся. – maschu

Смежные вопросы