× К оглавлению На главную Об авторе
## 1. Назначение

Дата и время публикации:    

Основная цель композиции и наследования в Python, обеспечить повторное использование и эффективное применение кода. **Композиция и наследование классов** В Python, [понимание](https://realpython.com/inheritance-composition-python/) наследования и композиции является крайне важным для эффективной работы с экземплярами классов. Наследование позволяет моделировать взаимосвязи, где производный класс расширяет функциональность базового . А вот композиция моделирует взаимосвязи, где класс содержит экземпляр другого класса, чтобы создать более сложную структуру. Обе технологии программирования способствуют повторному использованию кода, но достигают того по своему: * композиция и наследование в программировании моделирует взаимосвязи между классам, позволяя повторно использовать код различными способами; * Модели или схемы наследования является взаимосвязью, позволяющей производным классам расширять функциональность базового; * Наследование достигается с помощью определения классов, которые являются производными от базовых классов, наследуя их интерфейс и реализацию. **Наследование классов** Модель представления классов в качестве ящиков, где один ящик находится в другом и имеющих взаимосвязь, показанную стрелкой вниз от производного к базовому с обозначением назначения -- расширение функциональности, которую принято обозначать словом "Extends". (Обычно, gjrf ). Так же, наследование классов приянто обозначать с использованием унифицированного языка моделирования UML (рисунок справа):


     ┌──────────────────────────┐   ┌─────────────┐
     │ Derived сlass(base class)│   │ Base class  │
     ├───────────────────┬──────┤   ├─────────────┤
     │░░░░░░░░░░░░░░░░░░░│░░░░░░│   │             │
     │░░░░░░░░░░Extends░░│░░░░░░│   │             │
     │░░░░░░░░░░░░░┌─────▽──────┤   └──────△──────┘
     │░░░░░░░░░░░░░│ Base class │          │
     │░░░░░░░░░░░░░├────────────┤   ┌──────┴──────┐
     │░░░░░░░░░░░░░│▓▓▓▓▓▓▓▓▓▓▓▓│   │Derived class│
     │░░░░░░░░░░░░░│▓▓▓▓▓▓▓▓▓▓▓▓│   ├─────────────┤
     │░░░░░░░░░░░░░│▓▓▓▓▓▓▓▓▓▓▓▓│   │             │
     │░░░░░░░░░░░░░│▓▓▓▓▓▓▓▓▓▓▓▓│   │             │
     └─────────────┴────────────┘   └─────────────┘

В тоже время, рисунок слева дает понимание, что в Python наследование будет влиять на поведение экземпляров производного класса, но и сам класс, так о включает в себя атрибуты, свойства и методы класса и триггеры с двойным подчеркиванием (double leading underscores trigger), которые позволяют изменить поведение базового класса. **Композиция классов** В отличии от модели наследования, композиция позволяет создавать сложные классы путем комбинирования экземпляров классов через взаимосвязь, которая начинается от ромба у композитного класса (далее -- композит) и заканчивается стрелкой на компонентном классе (далее -- компонент):


    ┌─────────────┐
    │ Сomposite   │
    ├─────────────┤
    │             │
    │             │
    └──────◆──────┘
           │ attach
           V
    ┌─────────────┐
    │ Сomponent   │
    ├─────────────┤
    │             │
    │             │
    └─────────────┘

Cоздание взаимосвязи между классом Сomponent и Сomposite осуществляется с использованием метода attach(), определенного в классе Сomposite . **Шаблон проектирования Composite** В Python, негласно сложился [шаблон проектирования Composite](https://sbcode.net/python/composite/), который является структурным шаблоном для иерархического управления. Шаблон проектирования Composite, * Позволяет представлять индивидуальные сущности в виде листьев и группы листьев одним и тем же. * Является структурным шаблоном проектирования, который позволяет соединять экземпляры объектов в одну изменяемую структуру в виде дерева. * Является наилучшим, если вам нужна опция обмена иерархическими отношениями. * Позволяет добавлять / удалять компоненты в/из иерархии. * Обеспечивает гибкую структуру. _Интерфейс компонента_: интерфейс, у которого все листья и композиты должны быть реализованы. _Лист_: Отдельный экземпляр, что может существовать внутри или за пределами композита. _Композит_: Коллекция листьев или других композитов


    Композитная диаграмма UML
    ------------------------
    ┌─────────────┐            ┌─────────────┐                 Иерархическое дерево на основе композиции
    │             │            │ Сomponent   │<───┐            -----------------------------------------
    │ Client App  ├───────────▶├─────────────┤    │        extends    ┌───────────┐    extends
    │             │            │             │    │         ┌─────────▷ Сomponent ◁───────────┬───────┐
    └─────────────┘         ┌──▷             │    │         │         └───────────┘           │       │
                            │  └──────△──────┘    │         │                                 │       │
                   extends  │         │           │      ┌──┴─────────────┐ attach()  ┌───────┴────┐  │
                            │         │           │      │ Сomposite_top  ├──────────▶│ Composite  │  │
             ┌─────────────┐│  ┌──────┴──────┐    │      └────────────────┘           └──────────▲─┘  │
             │ Leaf        ├┘  │ Сomposite   ◆────┘                                     attach() │    │
             ├─────────────┤   ├─────────────┤                                                 ┌─┴────┴──────┐
             │             │   │             │                                                 │  Leaf       │
             │             │   │             │                                                 └─────────────┘
             └─────────────┘   └─────────────┘


На картинке показано композиционное иерархическое дерево со всеми возможными комбинированиями и взаимосвязями: * наследования к классу Сomponent, который является абстрактным базовым классом Сomposite_top, Composite и Leaf ; * композиции между классами Сomposite_top, Composite и Leaf с использованием интерфейса attach() . Шаблон проектирования Composite реализуется в Python c помощью модуля [abc — Abstract Base Classes](https://docs.python.org/3/library/abc.html), обеспечивающий инфраструктурой для определения абстрактных базовых классов (abstract base classes, ABCs) . Абстрактных базовые классы: * дополняют утиную типизацию, обеспечивая способ для определения интерфейсов, когда другие методы подобные hasattr() будут неуклюжими и не совсем правильными (например с [магическими методами](https://docs.python.org/3/reference/datamodel.html#special-lookup)); * вводят виртуальные подклассы, являющиеся классами, которые не наследуются от класса, но все же распознаются recognized by isinstance() and issubclass(). Python поставляется со многими встроенными классами ABC, такими как : * Структуры данных -- в модуле [collections.abc](https://docs.python.org/3/library/collections.abc.html#module-collections.abc)) * Чисел -- в модуле [numbers](https://docs.python.org/3/library/numbers.html#module-numbers)) * Потоковой обработки данных -- в модуле [io](https://docs.python.org/3/library/io.html#module-io)), * В поиске и загрузке импорта -- в модуле [importlib.abc module](https://docs.python.org/3/library/importlib.html#module-importlib.abc), В данной статье, речь пойдет о создании своего собственного класса ABC, как будет показано ниже, на основе модуля [abc](https://docs.python.org/3/library/abc.html) и класса [abc.ABC](https://docs.python.org/3/library/abc.html#abc.ABC).¶ **Композиция функций** В математике, композиция функций [является](https://python-course.eu/advanced-python/function-composition-in-python.php) операцией, которая комбинирует две или более функций в новой создаваемой функции. Композиция функций обозначается маленьким кругом (∘) или просто путем замещения одной функции другой. Данные две функции, например, f и g, являются композицей и обозначается как (f ∘ g)(x), определяется следующим образом:


    f: A -> B
    g: B -> C


Композиция из этих двух функций (f ∘ g) прорждает новую функцию, которая сопоставляет элементы из набора А к набору С . Эта композиция определяется для элемента x в наборе A следующим образом:


  (f ∘ g)(x)=f(g(x))


Композиция функций в Python является весьма схожей с математической концепцией, где композиция является комбинированием двух или более функций внутри одной, что в действительности является процессом превращения функции, которая берет множество аргуметов в последовательности функций, где каждой из них доступен один аргументе. ## 2. Использование ## 2.1 Реализация шаблона проектирования Composite Приводится на примере использования модуля [abc — Abstract Base Classes](https://docs.python.org/3/library/abc.html) и класса [abc.ABC](https://docs.python.org/3/library/abc.html#abc.ABC).¶


                Композиционная диаграмма UML
                ----------------------------
                                       ┌────────────────────────────┐
                                   ┌───▷  IСomponent                │<───┐
                                   │   ├────────────────────────────┤    │
                                   │   │ reference_to_parent: type  │    │
                                   │   │────────────────────────────│    │
                                   │   │ method(type): type         │    │
                                   │   │────────────────────────────│    │
                                   │   │ detach(type): type         │    │ attach()
                                   │   └───────────────△────────────┘    │
                                   │                   │                 │
                                   │                   │                 │
    ┌────────────────────────────┐ │   ┌───────────────┴────────────┐    │
    │          Leaf              ├─┘   │ IСomposite                 ◆────┘
    ├────────────────────────────┤     ├────────────────────────────┤
    │  type method(type)         │     │ components: list           │
    │────────────────────────────│     │────────────────────────────│
    │  type detach(type)         │     │ reference_to_parent: type  │
    └────────────────────────────┘     │────────────────────────────│
                                       │ method(type):  type        │
                                       │────────────────────────────│
                                       │ detach(type):  type        │
                                       │────────────────────────────│
                                       │ attach(type): type         │
                                       │────────────────────────────│
                                       │ delete(type): type         │
                                       └────────────────────────────┘

Шаблон проектирования Composite будет вестись на программных компонентах модуля [abc](https://docs.python.org/3/library/abc.html) :


    from abc import ABCMeta, abstractmethod


Где * [ABCMeta](https://docs.python.org/3/library/abc.html#abc.ABCMeta) -- Метакласс для определения абстрактного базового класса; * [abstractmethod](https://docs.python.org/3/library/abc.html#abc.abstractmethod) -- Декоратор абстрактного метода. **Класс IСomponent**


    class IComponent(metaclass=ABCMeta):

          reference_to_parent = None

          @staticmethod
          @abstractmethod
          def method():
              "A method each Leaf and composite container should implement"

          @staticmethod
          @abstractmethod
          def detach():
              "Called before a leaf is attached to a composite"


Является базовым классом и определяет или вернее объявляет два абстрактных метода, реализация которых будет выполнена позднее в производных классах Leaf и IСomposite : * method() должен реализовываться в производных классах Leaf и IСomposite; * detach() вызывается до присоединения экземпляра Leaf к экземпляру IСomposite . **Класс Leaf** Реализует method() и detach()


    class Leaf(IComponent):

        def method(self):
            parent_id = (id(self.reference_to_parent)
                           if self.reference_to_parent is not None else None)
            print( 
                   f"\t\tid:{id(self)}\tParent:\t{parent_id}"
                   )

        def detach(self):
             "Detaching this leaf from its parent composite"
              if self.reference_to_parent is not None:
                 print ( f"\t\tdetach from\t\t"
                         f"Parent: {id(self.reference_to_parent)}")
                 self.reference_to_parent.delete(self)


Где * интерфейс method() выводит свой и родительский id(), * интерфейс detach() отсоединяет себя от родительского экземпляра. Управление в части отсоединения от родительского можно осуществлять в присоединяемом экземпляре класса Leaf , такое же поведение интерфейс detach() можно наблюдать и в классе IComposite, который присоединяется к аналогичному экземпляру этого класса. **Класс IComposite** Композитный класс, реализуемый на базе абстрактного класса IСomponent, содержащий интерфейсы присоединения и удаления компонентных экземпляров на базе класса IComponent .


    class IComposite(IComponent):
        "A composite can contain leaves and composites"

        def __init__(self):
            self.components = []

        def method(self):
            parent_id = (id(self.reference_to_parent)
                         if self.reference_to_parent is not None else None)
            print(
                f"\tid:{id(self)}\tParent:\t{parent_id}\t"
                f"Components:{len(self.components)}")

            for component in self.components:
                component.method()

        def attach(self, component):
            """
            Detach leaf/composite from any current parent reference and
            then set the parent reference to this composite (self)
            """
            print ( f"\tattach  \t\tComponent: {id(component)}")
            component.detach()
            component.reference_to_parent = self
            self.components.append(component)

        def delete(self, component):
            "Removes leaf/composite from this composite self.components"
            self.components.remove(component)

        def detach(self):
            "Detaching this composite from its parent composite"
            if self.reference_to_parent is not None:
                self.reference_to_parent.delete(self)
                self.reference_to_parent = None


Где * интерфейс delete() удаляет экземпляры ранее присоединенные экземпляры компонентов, * интерфейс attach() не только присоединяет компонент с предварительной операцией отсоединения от другого композитного экземпляра. **Клиентское приложение** Создается на базе трех три экземпляра (два -- от класса Composite, один -- от класса Leaf ) на основе взаимосвязей наследования с классом IComponent:


     LEAF = Leaf()
     COMPOSITE_TOP=IComposite()
     COMPOSITE = IComposite()
     print(f"LEAF\t\tid:{id(LEAF)}")
     print(f"COMPOSITE_TOP\tid:{id(COMPOSITE_TOP)}")
     print(f"COMPOSITE\tid:{id(COMPOSITE)}")
     print()


Образует композитные взаимосвязи между двумя Composite и Leaf :


    COMPOSITE_TOP.attach(COMPOSITE)
    COMPOSITE.attach(LEAF)
    print()


Вызывает методы двух Composite и Leaf :


    LEAF.method()
    COMPOSITE.method()
    COMPOSITE_TOP.method()


Разрывает связь между Composite и Leaf :

 
    LEAF.detach()


В результате на выводе получается следующее:


    LEAF		    id:140336838737632    
    COMPOSITE_TOP	id:140336838737440
    COMPOSITE	    id:140336838736960

    	attach  		Component: 140336838737632
    	attach  		Component: 140336838736960
   
    		id:140336838737632	Parent:	140336838736960
    	id:140336838736960	Parent:	140336838737440	Components:1
    		id:140336838737632	Parent:	140336838736960
    	id:140336838737440	Parent:	None	Components:1
    	id:140336838736960	Parent:	140336838737440	Components:1
    		id:140336838737632	Parent:	140336838736960
    		detach from		Parent: 140336838736960


## 2.2 Композиция функций В Python, выполнение композиции функций осуществляется путем определения новой функции, которая комбинирует две или более функций, чтобы создать одну композитную функцию. Как показано в данном примере:


    >>> 
    >>> 
    >>> def f(x):
    ...     return 2 * x
    ...
    >>> def g(x):
    ...     return x + 3
    ...
    >>> # Define a composite function (f ∘ g)
    >>> def composite_function(x):
    ...     return f(g(x))
    ... 
    >>> # Test the composite function
    >>> result = composite_function(5)
    >>> print(result)
    16
    >>>