Наследование

Полиморфизм в случае duck typing всего один, зато тотальный ☺

Наследование

Просто:

   1 class New(Old):
   2     # поля и методы, возможно, перекрывающие Old.что-то-там

Видимость:

Вызов конструктора (например, для операция типа «"+"»):

   1 class A:
   2 
   3     def __add__(self, other):
   4         return type(self)(self.val + other.val)
   5 

Использование A(self.val + other.val) неправильно, т. к. подменяет тип.

   1 class B(A):
   2         pass

Какого типа должно быть B()+B()?

Родительский прокси-объект super()

Вызов методов базового класса:

   1 class A:
   2     def fun(self):
   3         return "A"
   4 
   5 class B(A):
   6     def fun(self):
   7         return super().fun()+"B"

Защита от коллизии имён

   1 >>> class C:
   2 ...     __A=1
   3 ...
   4 >>> dir(C)
   5 ['_C__A', '__class__', '__delattr__', …
   6 

Множественное наследование

Линеаризация

Создание линейного списка родительских классов для поиска полей в нём (Method Resolution Order, MRO).

MRO C3

Общий принцип

Пример

Пример значимо упрощённый, для полного понимания читайте документацию.

   1 O = object
   2 class F(O): pass
   3 class E(O): pass
   4 class D(O): pass
   5 class C(D,F): pass
   6 class B(D,E): pass
   7 class A(B,C): pass

Соответственно, нет решения для:

   1 class A: pass
   2 class B(A): pass
   3 class X(A,B): pass

Зато есть для class Y(B, A): …:

super():

   1 class A:
   2     def __new__(cls, *args):
   3     ¦   print(f"New A: {cls}, {args}")
   4     ¦   return super().__new__(cls, *args)
   5 
   6     def f(self):
   7     ¦   return f"A: {self}"
   8 
   9     def __str__(self):
  10     ¦   return f"<{type(self).__name__}>"
  11 
  12 class B:
  13     def __new__(cls, *args):
  14     ¦   print(f"New B: {cls}, {args}")
  15     ¦   return super().__new__(cls, *args)
  16 
  17     def g(self):
  18     ¦   return f"G: {self}"
  19 
  20     def __str__(self):
  21     ¦   return f"<<{type(self).__name__}>>"
  22 
  23 class AB(A, B):
  24     def __new__(cls, *args):
  25     ¦   print(f"New: {cls}, {args}")
  26     ¦   return super().__new__(cls, *args)
  27 
  28 ab = AB()
  29 print(ab, ab.f(), ab.g())

New: <class '__main__.AB'>, ()
New A: <class '__main__.AB'>, ()
New B: <class '__main__.AB'>, ()
<AB> A: <AB> G: <AB>

Обратите внимание на вызов обоих __new__() из super().__new__

Проксирование

Хранить родительский объект в виде поля, а все методы нового класса делать обёрткой вокруг методов родительского объекта.

Д/З

TODO