python: вызов супер () .__ init__ слишком рано в методе __init__? - PullRequest
2 голосов
/ 26 апреля 2011

У меня есть иерархия классов, где __init__ в class Base выполняет некоторую предварительную инициализацию и затем вызывает метод calculate. Метод calculate определен в class Base, но ожидается, что он будет переопределен в производных классах. Переопределенный calculate будет использовать некоторые атрибуты, которые доступны только в class Derived:

class Base:
    def __init__(self, args):
        # perform some pre-initialization
        ...
        # now call method "calculate"
        self.calculate()

class Derived(Base):
    def __init__(self, args, additional_attr):
        super().__init__(args)
        # do some work and create new instance attributes
        ...
        self.additional_attr = additional_attr

Это не сработает, потому что метод calculate в class Derived будет вызван до того, как будет назначен self.additional_attr.

Я не могу переместить super().__init__(args) вызов в конец метода __init__, потому что некоторая часть работы, которую он выполняет, должна выполняться до обработки additional_attr.

Что делать?

Ответы [ 4 ]

7 голосов
/ 26 апреля 2011

Возможно, у вас не должно быть вызова calculate() в вашем конструкторе. Если вы не можете сгенерировать производный объект, позволив сначала завершить базовый конструктор, то вы, должно быть, делаете что-то неправильно IMHO. Разумным подходом было бы убрать этот вызов из конструктора и, возможно, создать фабричный метод для автоматического выполнения этого вызова. Затем используйте этот метод, если вам нужны предварительно рассчитанные экземпляры.

class Base(object):
    def __init__(self, args):
        # perform some initialization
        pass
    def calculate(self):
        # do stuff
        pass
    @classmethod
    def precalculated(cls, args):
        # construct first
        newBase = cls(args)
        # now call method "calculate"
        newBase.calculate()
        return newBase

class Derived(Base):
    def __init__(self, args, additional_attr):
        super(Derived, self).__init__(args)
        # do some work and create new instance attributes
        self.additional_attr = additional_attr
    @classmethod
    def precalculated(cls, args, additional_attr): # also if you want
        newDerived = cls(args, additional_attr)
        newDerived.calculate()
        return newDerived

newBase = Base('foo')
precalculatedBase = Base.precalculated('foo')
newDerived = Derived('foo', 'bar')
precalculatedDerived = Derived.precalculated('foo', 'bar')
4 голосов
/ 26 апреля 2011

Это плохой дизайн, ИМХО, и вы злоупотребляете объектной системой Python. Учтите, что в других языках OO, таких как C ++, вы даже не можете контролировать создание базовых классов. Конструктор производного класса вызывает базовый конструктор до вашего кода. Такое поведение почти всегда ожидается от хорошо управляемых иерархий классов, и изменение его может привести только к проблемам.

Конечно, вы можете сделать некоторые исправления (например, присвоить self.additional_attr перед вызовом конструктора super или другие приемы), но лучшим способом было бы изменить ваш дизайн так, чтобы он не требовал такие хаки. Поскольку вы привели здесь абстрактный пример, трудно дать более исчерпывающий совет по дизайну.

2 голосов
/ 26 апреля 2011

Для того, чтобы что-то подобное работало, вам необходимо разработать протокол, который позволит базовому и производному классу (-ам) взаимодействовать друг с другом для выполнения задачи инициализации объекта:

class Base:
    def __init__(self, args, *additional_args):
        # perform some pre-initialization
        # ...

        # perform any futher initialization needed by derived classes
        self.subclass_setup(*additional_args)

        # now call method "calculate"
        self.calculate()

    def subclass_setup(self, *args):
        pass

class Derived(Base):
    def __init__(self, args, additional_attr):
        super().__init__(args, additional_attr)

    def subclass_setup(self, additional_attr):
        # do some work and create new instance attributes
        # ...
        self.additional_attr = additional_attr
1 голос
/ 26 апреля 2011

Можете ли вы передать additional_attr в качестве параметра методу __init__ базового класса и распространить его оттуда на calculate метод?

Сказать что-то вроде:

class Base(object): 
    def __init__(self, args,additional_attr): 
        print 'Args for base class:%s' %(args)
        self.calculate(additional_attr)

class Derived(Base):
    def __init__(self, args, additional_attr):
        super(Derived,self).__init__(args,additional_attr)

     def calculate(self,val):
         print 'Arg for calculate:%s' %(val)
         self.additional_attr = val
>>> d = Derived(['test','name'],100)
Args for base class:['test', 'name']
Arg for calculate:100

Это окольный путь, но без информации о том, каковы этапы предварительной инициализации, трудно сказать, поможет ли вышеуказанный подход вам.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...