На практике часто возникает ситуация, когда в предметной области выделены очень близкие, но вместе с тем неодинаковые классы. Одним из способов сокращения описания классов за счет использования их сходства является выстраивание классов в иерархию. В корне этой иерархии стоит базовый класс, от которого нижележащие классы иерархии наследуют свои атрибуты, уточняя и расширяя поведение вышележащего класса. Обычно принципом построения классификации является отношение "IS-A" ("ЕСТЬ"). Например, класс Окружность в программе - графическом редакторе может быть унаследован от класса Геометрическая Фигура. При этом Окружность будет являться подклассом (или субклассом) для класса Геометрическая Фигура, а Геометрическая Фигура - надклассом (или суперклассом) для класса Окружность.
В языке Python во главе иерархии ("новых") классов стоит класс object. Для ориентации в иерархии существуют некоторые встроенные функции, которые будут рассмотрены ниже. Функция issubclass(x, y) может сказать, является ли класс x подклассом класса y:
>>> class A(object): pass ... >>> class B(A): pass ... >>> issubclass(A, object) True >>> issubclass(B, A) True >>> issubclass(B, object) True >>> issubclass(A, str) False >>> issubclass(A, A) # класс является подклассом самого себя True
В основе построения классификации всегда стоит принцип, играющий наиболее важную роль в анализируемой и моделируемой системе. Следует заметить, что одним из "перегибов" при использовании ОО методологии является искусственное выстраивание иерархии классов. Например, не стоит наследовать класс Машина от класса Колесо (внимательные заметят, что здесь отношение другое: колесо является частью машины).
Класс называется абстрактным, если он предназначен только для наследования. Экземпляры абстрактного класса обычно не имеют большого смысла. Классы с рабочими экземплярами называются конкретными.
В Python примером абстрактного класса является встроенный тип basestring, у которого есть конкретные подклассы str и unicode.
В отличие, например, от Java, в языке Python можно наследовать класс от нескольких классов. Такая ситуация называется множественным наследованием (multiple inheritance).
Класс, получаемый при множественном наследовании, объединяет поведение своих надклассов, комбинируя стоящие за ними абстракции.
Использовать множественное наследование следует очень осторожно, а необходимость в нем возникает реже одиночного.
В случае с Python наследование можно считать одним из способов собрать нужные комбинации методов в серии классов:
class A: def a(self): return 'a' class B: def b(self): return 'b' class C: def c(self): return 'c' class AB(A, B): pass class BC(B, C): pass class ABC(A, B, C): pass
Впрочем, собрать нужные методы можно и по-другому, без использования наследования:
def ma(self): return 'a' def mb(self): return 'b' def mc(self): return 'c' class AB: a = ma b = mb class BC: b = mb c = mc class ABC: a = ma b = mb c = mc
В случае, когда надклассы имеют одинаковые методы, использование того или иного метода определяется порядком разрешения методов (method resolution order). Для "новых" классов узнать этот порядок очень просто с помощью атрибута __mro__:
>>> str.__mro__ (<type 'str'>, <type 'basestring'>, <type 'object'>)
Это означает, что сначала методы ищутся в классе str, затем в basestring, а уже потом - в object.
Для "классических" классов порядок несколько отличается от порядка разрешения методов в "новых" классах. Нужно стараться избегать множественного наследования или применять его очень аккуратно.