Теперь можно задаться целью построить класс, экземплярами которого будут классы. Такой класс, от которого порождаются классы, и называется метаклассом.
В Python имеется класс type, который на деле является метаклассом. Вот как с помощью его конструктора можно создать класс:
Листинг
def my_method(self):
print «self:", self
My_Class = type('My_Class', (object,), {'my_method': my_method})
В качестве первого параметра type передается имя класса, второй параметр — базовые классы для данного класса, третий — атрибуты.
В результате получится класс, эквивалентный следующему:
Листинг
class My_Class(object):
def my_method(self):
print «self:", self
Но самое интересное начинается при попытке составить собственный метакласс. Проще всего наследовать метакласс от метакласса type (пример взят из статьи Дэвида Мертца):
Листинг
>>> class My_Type(type):
… def __new__(cls, name, bases, dict):
… print «Выделение памяти под класс», name
… return type.__new__(cls, name, bases, dict)
… def __init__(cls, name, bases, dict):
… print «Инициализация класса», name
… return super(My_Type, cls).__init__(cls, name, bases, dict)
…
>>> my = My_Type(«X», (), {})
Выделение памяти под класс X
Инициализация класса X
В этом примере не происходит вмешательство в создание класса. Но в __new__() и __init__() имеется полный программный контроль над создаваемым классом в период выполнения.
Примечание:
Следует заметить, что в метаклассах принято называть первый аргумент методов не self, а cls, чтобы напомнить, что экземпляр, над которым работает программист, является не просто объектом, а классом.
Мультиметоды
Некоторые объектно–ориентированные «штучки» не входят в стандартный Python или стандартную библиотеку. Ниже будут рассмотрены мультиметоды — методы, сочетающие объекты сразу нескольких различных классов. Например, сложение двух чисел различных типов фактически требует использования мультиметода. Если «одиночный» метод достаточно задать для каждого класса, то мультиметод требует задания для каждого сочетания классов, которые он обслуживает:
Листинг
>>> import operator
>>> operator.add(1, 2)
3
>>> operator.add(1.0, 2)
3.0
>>> operator.add(1, 2.0)
3.0
>>> operator.add(1, 1+2j)
(2+2j)
>>> operator.add(1+2j, 1)
(2+2j)
В этом примере operator.add ведет себя как мультиметод, выполняя разные действия для различных комбинаций параметров.
Для организации собственных мультиметодов можно воспользоваться модулем Multimethod (автор Neel Krishnaswami), который легко найти в Интернете. Следующий пример, адаптированный из документации модуля, показывает построение собственного мультиметода:
Листинг
from Multimethod import Method, Generic, AmbiguousMethodError
# классы, для которых будет определен мультиметод
class A: pass
class B(A): pass
# функции мультиметода
def m1(a, b): return 'AA'
def m2(a, b): return 'AB'
def m3(a, b): return 'BA'
# определение мультиметода (без одной функции)
g = Generic()
g.add_method(Method((A, A), m1))
g.add_method(Method((A, B), m2))
g.add_method(Method((B, A), m3))
# применение мультиметода
try:
print 'Типы аргументов:', 'Результат'
print 'A, A:', g(A(), A())
print 'A, B:', g(A(), B())
print 'B, A:', g(B(), A())
print 'B, B:', g(B(), B())
except AmbiguousMethodError:
print 'Неоднозначный выбор метода'
Устойчивые объекты
Для того чтобы объекты жили дольше, чем создавшая их программа, необходим механизм их представления в виде последовательности байтов. Во второй лекции уже рассматривался модуль pickle, который позволяет сериализовать объекты.
Здесь же будет показано, как класс может способствовать более качественному консервированию объекта. Следующие методы, если их определить в классе, позволяют управлять работой модуля pickle и рассмотренной ранее функции глубокого копирования. Другими словами, правильно составленные методы дают возможность воссоздать объект, передав самую суть — состояние объекта.
__getinitargs__() Должен возвращать кортеж из аргументов, который будет передаваться на вход метода __init__() при создании объекта.
__getstate__() Должен возвращать словарь, в котором выражено состояние объекта. Если этот метод в классе определен, то используется атрибут __dict__, который есть у каждого объекта.
__setstate__(state) Должен восстанавливать объекту ранее сохраненное состояние state.
В следующем примере класс CC управляет своим копированием (точно так же экземпляры этого класса смогут консервироваться и расконсервироваться при помощи модуля pickle):
Листинг
from time import time, gmtime
import copy
class CC:
def __init__(self, created=time()):
self.created = created
self.created_gmtime = gmtime(created)
self._copied = 1
print id(self), «init», created
def __getinitargs__(self):
print id(self), «getinitargs», self.created
return (self.created,)
Читать дальше