Мы
продолжаем тему метаклассов. На этом занятии я хочу привести один более-менее
практический пример их использования, чтобы вы лучше понимали их роль в
программировании.
Фреймворк
Django использует метаклассы для
связи объектов с записями БД. То есть, метаклассы используются для реализации
его API для ORM. В
частности, он делает следующее. Можно определить класс модели с набором
атрибутов, которые будут представлять соответствующие поля таблицы БД.
Например, предположим, что имеется класс Women для
работы с таблицей, содержащей поля title, content, photo и
т.д.:
class Women(models.Model):
title = models.CharField(max_length=255)
content = models.TextField(blank=True)
photo = models.ImageField(upload_to="photos/%Y/%m/%d/")
time_create = models.DateTimeField(auto_now_add=True)
time_update = models.DateTimeField(auto_now=True)
is_published = models.BooleanField(default=True)
Затем,
во фреймворке Django нам достаточно создать экземпляр этого класса:
w = Women(title='Энн Хэтэуэй')
чтобы
прочитать все записи содержащие поле title равное 'Энн Хэтэуэй'.
Причем сами данные появляются автоматически в локальных свойствах объекта w. То есть, далее
мы можем обратиться к данным из таблицы, например, так:
И так далее. Вот
эта магия по созданию объекта, в котором сразу оказываются нужные данные с
соответствующими наборами локальных свойств и выполняется на уровне
метаклассов. В действительности, базовый класс Model, как раз и
реализует этот метакласс, добавляя необходимый функционал в класс модели Women.
Это конкретный
пример, где используются метаклассы. Чтобы все было более понятно, давайте
сделаем метакласс, который бы позволял при создании объектов класса
автоматически добавлять в него локальные свойства с теми же именами, что и
атрибуты класса. То есть, мы повторим некоторый функционал API ORM Django.
Вначале
определим метакласс, в котором пропишем методы create_local_attrs и __init__:
class Meta(type):
def create_local_attrs(self, *args, **kwargs):
for key, value in self.class_attrs.items():
self.__dict__[key] = value
def __init__(cls, name_class, base, attrs):
cls.class_attrs = attrs
cls.__init__ = Meta.create_local_attrs
Первый
метод create_local_attrs будет в дальнейшем являться инициализатором создаваемого класса.
И в этом инициализаторе мы, как раз и создаем локальные свойства в экземплярах
класса. Для этого в самом классе мы создаем специальный словарь class_attrs,
который содержит все атрибуты класса с их значениями. Фактически, класс Women у нас теперь становится таким:
class Women:
class_attrs = {'title': 'заголовок', 'content': 'контент', 'photo': 'путь к фото'}
title = 'заголовок'
content = 'контент'
photo = 'путь к фото'
def __init__(self, *args, **kwargs):
for key, value in self.class_attrs.items():
self.__dict__[key] = value
Конечно,
метаклассы во фреймворке Django
гораздо изощреннее нашего с вами примера. Я здесь лишь хотел показать саму идею
их использования при создании различных API. А,
вообще, конечно следует избегать их применения на практике, так как они
несколько запутывают программный код и являются источником трудно отслеживаемых
ошибок. Здесь всегда следует держать в голове высказывание гуру Питона Тима
Питерса:
Метаклассы
– это глубокая магия, о которой 99% пользователей даже не нужно задумываться.
Если вы думаете, нужно ли вам их использовать — вам не нужно (люди, которым они
реально нужны, точно знают, зачем они им, и не нуждаются в объяснениях,
почему).
Надеюсь,
они вам не понадобятся, а если понадобятся, то вы теперь имеете представление,
что это такое.