Я не знаю, как это делает Джанго.Но вы можете сделать это следующим образом:
class WantFixup(object):
def new_instance(self, name, derived_name):
cls = type(self)
if name is None:
name = derived_name.replace('_', ' ')
return cls(name)
class Container(WantFixup):
def __init__(self, name=None):
self.name = name
def __repr__(self):
return "Container('%s')" % str(self.name)
class WillFixup(object):
def __init__(self):
cls = type(self)
for name in cls.__dict__:
o = cls.__dict__[name] # look up object from name
if not isinstance(o, WantFixup):
continue
print("calling %s.new_instance('%s', '%s')" % (o, o.name, name))
self.__dict__[name] = o.new_instance(o.name, name)
class Name(WillFixup):
first_name = Container("given name")
last_name = Container()
Вот пример приведенного выше кода в действии:
>>> import auto_name
>>> n = auto_name.Name()
calling Container('None').new_instance('None', 'last_name')
calling Container('given name').new_instance('given name', 'first_name')
>>> print(n.__dict__)
{'first_name': Container('given name'), 'last_name': Container('last name')}
>>> print(auto_name.Name.__dict__)
{'__module__': 'auto_name', 'last_name': Container('None'), 'first_name': Container('given name'), '__doc__': None}
>>>
Класс WantFixup
служит двум целям.Во-первых, все классы, которые наследуются от него, могут быть обнаружены с помощью isinstance()
;если наш экземпляр объекта называется o
, мы можем проверить его как isinstance(o, WantFixup)
.Во-вторых, он предоставил функцию метода .new_instance()
любому классу, который унаследовал ее.
Класс Container
является примером контейнера, который может нуждаться в исправлении.Обратите внимание, что он наследуется от WantFixup
.
. Класс WillFixup
содержит метод .__init__()
, который выполняет исправление для всех классов, которые наследуются от него.Это просто перебирает все в словаре классов и вызывает для каждого из них функцию метода .new_instance()
, передавая имя.
Наконец, класс Name
наследуется от WillFixup
и содержит два экземпляра Container
.Поскольку он наследуется от WillFixup
, вызывается метод WillFixup.__init__()
.Как видно из примера, first_name
имеет атрибут .name
, установленный на 'given name'
, но last_name
не был установлен, поэтому он исправлен, чтобы его атрибут .name
был установлен на 'last name'
.
Функция .__init__()
должна устанавливать новый экземпляр класса.Пока все особые экземпляры класса WantFixup
находятся в родительском классе, метод .__init__()
будет автоматически зацикливать их и устанавливать их.
Запутанная часть в том, что экземпляр имеет first_name
установить экземпляр Container
, имя которого исправлено и будет фактически использоваться для хранения вещей.Но класс Name
содержит экземпляр Container
, который просто используется для хранения имени класса и в качестве маркера для метода .__init__()
для поиска.
Хорошая часть заключается в том, чтомагия скрыта в базовых классах.Классы Container
и Name
просто должны наследоваться от них, но сами по себе они не загромождены вещами.
Может быть более изощренный способ решения проблемы с помощью метапрограммирования.
http://www.ibm.com/developerworks/linux/library/l-pymeta/index.html
Это решение не является программированием метакласса, но оно протестировано, рабочий код.
РЕДАКТИРОВАТЬ: Это измененная версия кода.Первоначальный код был предназначен для демонстрации общей идеи, но фактически не инициировал объект Name
.На самом деле не сложно выполнить инициализацию, поэтому я изменил ее.