Почему механизм наследования Python позволяет мне определять классы, экземпляры которых не создаются должным образом?

Прошу прощения за довольно расплывчатую формулировку вопроса. Я попытаюсь пояснить, что я имею в виду, на простом примере ниже. У меня есть класс, который я хочу использовать в качестве базы для других классов:

class parent:
    def __init__(self, constant):
        self.constant = constant
    def addconstant(self, number):
        return self.constant + number

Параметр self.constant имеет первостепенное значение для удобства использования класса, так как от него зависит метод addconstant. Поэтому метод __init__ заботится о том, чтобы заставить пользователя определить значение этого параметра. Все идет нормально.

Теперь я определяю дочерний класс следующим образом:

class child(parent):
    def __init__(self):
        pass

Ничто не мешает мне создать экземпляр этого класса, но когда я попытаюсь использовать addconstant, он явно вылетит.

b = child()
b.addconstant(5)
AttributeError: 'child' object has no attribute 'constant'

Почему Python позволяет это делать!? Я чувствую, что Python слишком гибок и каким-то образом нарушает суть ООП и инкапсуляции. Если я хочу расширить класс, чтобы воспользоваться преимуществами наследования, я должен быть осторожен и знать некоторые детали реализации класса. В этом случае я должен знать, что принуждение пользователя к установке константы параметра constant имеет основополагающее значение, чтобы не нарушать удобство использования класса. Не нарушает ли это как-то принцип инкапсуляции?


person Onturenio    schedule 19.02.2021    source источник
comment
Python возлагает на пользователя большую ответственность за то, чтобы он был «ответственным взрослым», например. с едва защищенными «частными» атрибутами и тому подобное. Не для того, чтобы нянчиться с тобой…   -  person deceze♦    schedule 19.02.2021
comment
Во многом причина, по которой люди любят Python, заключается в том, что он такой гибкий и на самом деле не запрещает многие вещи. Итак, если вы хотите вызвать родительский инициализатор, то вызовите его, если нет, то не делайте этого.   -  person wim    schedule 19.02.2021
comment
Если я хочу расширить класс, чтобы воспользоваться преимуществами наследования, я должен быть осторожен и точно знать некоторые детали реализации класса!   -  person Cyttorak    schedule 19.02.2021
comment
Инкапсуляция больше относится к Java, чем к Python.   -  person M-Chen-3    schedule 19.02.2021
comment
Также обратите внимание, что само определение класса не определяет атрибут constant; вызов parent.__init__ делает.   -  person chepner    schedule 19.02.2021


Ответы (1)


Потому что Python — динамический язык. Он не знает, какие атрибуты находятся в экземпляре parent, пока инициализатор parent не поместит их туда. Другими словами, атрибуты parent определяются при создании экземпляра parent, а не мгновением ранее.

В таком языке, как Java, атрибуты parent будут установлены во время компиляции. Но определения классов Python выполняются, а не компилируются.

На практике это вообще не проблема. Да, если вы забудете вызвать инициализатор родительского класса, у вас будут проблемы. Но вызов инициализатора родительского класса — это то, что вы почти всегда делаете на любом языке, потому что вам нужно поведение родительского класса. Итак, не забудьте сделать это.

Иногда полезной техникой является определение разумного значения по умолчанию для самого класса.

class parent:
    constant = 0
    def __init__(self, constant):
        self.constant = constant
    def addconstant(self, number):
        return self.constant + number

Python возвращается к доступу к атрибуту класса, если вы не определили его в экземпляре. Таким образом, это обеспечит резервное значение на случай, если вы забудете это сделать.

person kindall    schedule 19.02.2021
comment
Я бы сказал, что резервный вариант класса по умолчанию просто скрывает ошибку. Теперь вы не увидите сообщение об ошибке, но оно, вероятно, будет вести себя по-другому. - person deceze♦; 19.02.2021
comment
Зависит от варианта использования, но да, я вижу, что здесь предпочтение отдается быстрому сбою. - person kindall; 19.02.2021