Наследование
Долги за прошлый раз:
hasattr() и getattr()
__iter__()
__del__()
Наследование
Объектное планирование и ООП
ООП:
Характеристика |
Python3 |
Инкапсуляция (не сокрытие) |
Иерархия пространств имён |
Наследование |
Наследование + C3 MRO |
Полиморфизм |
«Из коробки», т. к. duck typing |
Проксирование?
Хранить родительский объект в виде поля, а все методы нового класса делать обёрткой вокруг методов родительского объекта.
TODO пример
Простое наследование
- Видимость и перегрузка методов
- Преобразование типов и создание новых объектов текущего типа:
type(self)(…)
- Вызов метода родительского класса
super() — прокси-объект, аккумулирующий методы всех родительских классов (см. множественное наследование)
Защита полей от случайной перегрузки («__»)
Множественное наследование
- Проблема ромбовидного наследования:
Обход в глубину добирается до A.v раньше, чем до C.v
Обход в ширину добирается до A.v раньше, чем до B.v
Что нужно? Линеаризация:
- Монотонность C: [C, …, B, …, A] ⇒ D(...(C)...): [D, …, C, …, B, …, A]
Соблюдение порядка объявления: class C(D,E,F): … ⇒ `[C, D, E, F, …]
- ⇒ Некоторые ситуации невозможны
- MRO C3
- Общий принцип
- Линеаризация графа наследования классов — это объединение списка линеаризаций всех непосредственных родительских классов
- Объединение — это упорядочивание списка линеаризаций по следующему принципу:
- Рассматриваем список слева направо
Если очередной класс не является ничьим предком из списка, он добавляется в линеаризацию, а из списка удаляется
- переход к п. 1.
- Если очередной класс является чьим-то предком (входит в какую-то линеаризацию не в начало), переходим к следующему классу
- Если хороших кандидатов не нашлось, линеаризация невозможна
- Пример (слегка упрощённый):
- Простое наследование (L[X] — линеаризация класса X):
L[O] = O L[D] = D O L[E] = E O L[F] = F O
- Множественное наследование
L[B] = B + merge(DO, EO) D? Good L[B] = B + D + merge(O, EO) O? Not good (EO) E? Good L[B] = B + D + E + merge(O, O) O? Good L[B] = BDEO
соответственно,L[C] = CDFO
наконец,L[A]: A + merge(BDEO,CDFO) B? + A + B + merge(DEO,CDFO) D? × C? + A + B + C + merge(DEO,DFO) D? + A + B + C + D + merge(EO,FO) E? + A + B + C + D + E + merge(O,FO) F? + A + B + C + D + E + F + merge(O,O) O? + ABCDEFO
То есть: Но если (B(E,D) вместо B(D,E)):
то
- Простое наследование (L[X] — линеаризация класса X):
super(): как всегда — объект-прокси всех методов родительских классов, в в случае множественного наследования аналогов не имеет (это как бы объект несуществующего класса)
Д/З
Повторить 15-ю и 16-ю главы учебника, прочитать и прощёлкать 17-ю и 18-ю главы учебника
- Прочитать статьи про линеаризацию
Зачем в описании линеаризации участвует объединение линеаризаций родительских классов и самих классов (в лекциях я это опустил)?
- Иными словами, чему служит выделенная часть:
L[C(B1 ... BN)] = C + merge(L[B1] ... L[BN], B1 ... BN)
- Много ли мы теряем от того, что C3, в отличие от обхода дерева, может завершиться неудачей?
- Прочитать статьи про линеаризацию
EJudge: DivStr 'Деление строки'
Написать класс DivStr, унаследованный от str, который поддерживал бы операцию деления «//» и остатка от деления «%». Деление на N должно возвращать список из N подстрок одинаковой наибольшей длины, на которые можно разделить исходную строку, а остаток — оставшуюся концевую подстроку меньшей длины (возможно, пустую)
XcDf QWEa sdER Tdfg RTY
EJudge: DefCounter 'Счётчик с умолчанием'
(исследовательская задача) Написать класс DefCounter, унаследованный от collections.Counter, в котором значения для несуществующих элементов были бы не 0, а задавались в конструкторе именным параметром missing= (по умолчанию — -1).
A = DefCounter("QWEqweQWEqweQWE", missing=-10) print(A) print(A["Z"]) A["P"]+=5 print(A)
DefCounter({'Q': 3, 'W': 3, 'E': 3, 'q': 2, 'w': 2, 'e': 2}) -10 DefCounter({'Q': 3, 'W': 3, 'E': 3, 'q': 2, 'w': 2, 'e': 2, 'P': -5})
EJudge: LetterAttr 'Буквенное поле'
Написать класс LetterAttr, в котором будут допустимы поля с любым именем; значение каждого поля по умолчанию будет совпадать с именем поля (строка), а при задании нового строкового значения туда будут попадать только буквы, встречающиеся в имени поля.
letter digit teller rteteller