class A:
def __init__(self):
self.j = 0
self.calc_i(865)
def calc_i(self, i):
self.i = 68 * i
class B(A):
def __init__(self):
super().__init__()
print("i from B is", self.i)
def calc_i(self, i):
self.i = 37 * i
b = B()
Приведенный выше код должен вызывать метод родительского класса calc_i в соответствии с моим пониманием, потому что родительский класс будет запускать свой собственный метод, и он также не знает, где он используется в качестве родителя.
Но когда я запускаю его, он вызывает дочерний метод, где я * 37, а не родительский метод
Когда вы переопределяете метод, он заменяет версию родительского класса вашей собственной; оригинал больше не вызывается автоматически. Если вы хотите, чтобы он вызывался явно, вам нужно написать явный код для этого, либо используя super, либо ожидая вызова A.calc_i. Заставляя его ожидать, вы можете выбрать, хотите ли вы вызывать родительскую версию до, после или где-то в середине вашей собственной (и аналогичным образом позволяет вам делать то, что вы делаете сейчас, и вообще не вызывать родитель).
То, что self.calc_i вызывается A.__init__, не означает, что self.calc_i разрешается в A.calc_i. Вы не знаете, каким будет тип времени выполнения self, и именно это определяет, какой метод будет вызываться.






потому что родительский класс будет запускать собственный метод
Нет, не будет.
Когда B.__init__ вызывает super().__init__, это тот же экземпляр B, который передается A.__init__. В результате оценка self.calc_i использует порядок разрешения метода (MRO) B, а не A, чтобы определить, какой calc_i метод вызывается. Поскольку MRO — это [B, A, object], а B определяет calc_i, этот метод вызывается self.calc_i(865).
Самое важное, что нужно помнить при написании метода, это то, что вы не знаете тип времени выполнения self. Единственное, в чем вы можете быть уверены, так это в том, что type(self) будет подклассом A, и вы не можете предсказать, какие методы будет определять этот подкласс.
Если вам абсолютно необходимо A.__init__ для вызова A.calc_i, вы должны быть явными:
def __init__(self):
self.j = 0
A.calc_i(self, 865)
или определите метод, имя которого абсолютно ясно дает понять, что его не следует (и нельзя без некоторых усилий) переопределять.
def __init__(self):
self.j = 0
self.__calc_i(self, 865)
def __calc_i(self, i):
self.i = 68 * i
Если вы хотите, чтобы некоторые варианты использования calc_i были переопределяемыми, вы можете определить
def calc_i(self, i):
return self.__calc_i(i)
и используйте self.__calc_i, когда это должна быть частная реализация A, и self.calc_i, когда вы согласны с подклассом, предоставляющим свой собственный.
Написав
calc_i()для ребенка, вы переопределяете это имя еще до того, как будет вызвано__init__().