Мы уже немало говорили об объектно-ориентированном программировании. Создание своих собственных классов, наследование, прототипирование - это очень удобно. Но это ещё не всё. Python, будучи объектно-ориентированным языком, делает парадигму ООП более гибкой, чем это может показаться на первый взгляд. Сейчас Мы разберём компонентное программирование - ещё один стиль ООП, который вполне можно реализовать на Python.
Компонентное программирование(КП) предусматривает использование компонентов - определённых структурированных частей кода. Эти компоненты подключаются к уже готовым объектам, расширяя их возможности. Разбирая декораторы, Мы говорили о том, что в Python'е функции и даже классы являются объектами. Это свойство Нам сейчас и пригодится.
Для начала напишем простенький класс:
| Python | 1
2
3
4
5
| class chameleon:
def __init__(self,color,age,weight):
self.color=color
self.age=age
self.weight=weight |
|
Давайте с помощью функции dir() выведем список атрибутов этого класса и оставим его где-нибудь на видном месте.
Теперь опишем компонент - функцию. При этом по форме своей она будет похожа на метод:
| Python | 1
2
| def chcolor(self,col):
self.color=col |
|
Функция - это объект, который Мы можем присвоить какой-нибудь переменной. Например, Мы можем присвоить её классу в качестве атрибута:
| Python | 1
| chameleon.chcolor=chcolor |
|
Давайте ещё раз воспользуемся dir(chameleon) и посмотрим на новый список атрибутов. Появился новый атрибут - chcolor. Это Наш компонент. Дабы убедиться в том, что компонент правильно встроился в класс, инстанцируем объект и обратимся к методу chcolor():
| Python | 1
2
3
| cham=chameleon('red',5,50)
cham.chcolor('blue')
print(cham.color) |
|
Как видите, объект cham использует функцию chcolor() как свой собственный метод. При этом сама функция может находиться где угодно за пределами описания класса. Но это ещё не всё. Попробуйте сначала инстанцировать объект, а потом присвоить классу(не объекту!) ещё один метод. Удивительно, но готовый объект так же приобретёт новый метод. Некоторое неудобство заключается в том, что все объекты, принадлежащие классу chameleon(), приобретут этот новый метод. О том, как этого избежать, Мы поговорим чуть ниже. А сейчас давайте, наоборот, удалим метод. Для этого Нам понадобится оператор del:
Заметьте, все экземпляры класса chameleon(), потеряют удалённый метод.
С трансформацией классов всё оказалось просто. Но иногда нужно изменить не весь класс, а только один его экземпляр. Обычное присваивание тут не поможет. Казалось бы, почему нет!? Сейчас разберёмся. Используем тот же класс chameleon() и функцию chcolor():
| Python | 1
2
3
| ch=chameleon('red',5,50)
ch.chcolor=chcolor
ch.chcolor('yellow') |
|
В ответ Мы получим что-то, вроде: "TypeError: chcolor() missing 1 required positional argument: 'col'". Если Вы не забыли, методы получают сущность объекта, которому они принадлежат, первым аргументом, который принято называть self. Передача происходит неявно. Однако, в Нашем случае, функция остаётся функцией, а значит, никакого self она получать не будет. Таким образом, функция принимает первый аргумент self явно, присваивая ему значение 'yellow'. Можно, конечно, избежать этого, каждый раз явно передавая объект первым аргументом, но это не удобно.
Убедиться в том, что после присваивания функция осталась функцией можно с помощью инструкции:
Ответом будет <class 'function'>. Если Вы используете эту конструкцию для получения класса метода, то обнаружите, что метод(<class 'method'>) и функция - это не одно и то же. У методов, есть атрибут __self__, который при вызове неявно указывает объект self. Но, даже добавив функции аналогичный атрибут __self__, указывающий на объект, Вы не решите проблему.
Поможет Нам в данном случае модуль types. В нём содержится класс MethodType(), который исправит все недоразумения, тревожащие Нас на протяжении последних двух абзацев:
| Python | 1
2
3
4
5
6
7
8
9
10
11
12
| from types import MethodType
class clameleon:
def __init__(self,color,age,weight):
self.color=color
self.age=age
self.weight=weight
def chcolor(self,col):
self.color=col
obj=chameleon('red',5,50)
obj.chcolor=MethodType(chcolor,obj) |
|
Вот это уже на что-то похоже. Теперь объект obj приобрёл полноценный метод chcolor(). Заметьте, что MethodType() принимает два аргумента: функцию и объект, к которому будет привязана функция. При этом, изменения будут проведены только в объекте obj. Инстанцировав второй объект, Вы обнаружите, что метода chcolor() он не имеет. То есть, сам класс остался нетронутым.
С удалением методов проблем нет. Нам понадобиться тот же оператор del, который без лишних заморочек удалит любой атрибут уже готового объекта.
На этом Мы закончим. Не проходите мимо компонентного программирования. Ведь модификация класса в любой точке программного кода очень удобна. Вам не нужно искать описание класса, возможно, находящееся в далеко сидящем модуле. Ну а трансформинг готовых объект - это просто вишенка на торте.
|