Как динамически добавлять атрибуты в интерфейс
Мне нужно добавить атрибут для каждого атрибута в интерфейсе. Поэтому я пытаюсь динамически изменить его, чтобы добавить их, но пока без особого успеха.
Допустим, у меня есть следующий интерфейс:
class IMember(Interface):
first_name = schema.TextLine(title=u'first name')
last_name = schema.TextLine(title=u'last name')
И я хотел бы изменить это так:
class IMember(Interface):
first_name = schema.TextLine(title=u'first name')
last_name = schema.TextLine(title=u'last name')
visbility_first_name = schema.Bool(title=u'Display: first name')
visbility_last_name = schema.Bool(title=u'Display: last name')
Потом я попытался изменить класс, но, поскольку он уже был инициализирован, схема была установлена, и я не был уверен, как ее изменить. Я также подумал о написании директивы (например: interface.Implements()), но кажется, что это довольно сложно сделать просто для добавления атрибутов.
Моя последняя цель - добавить набор полей z3c.form с набором виджетов Bool.
Итак, есть ли способ сделать это в Python, или мне нужно изменить интерфейс и добавить все атрибуты вручную?
Спасибо!
2 ответа
Вы можете создать динамический подкласс интерфейса, используя InterfaceClass
метатип.
Создайте словарь из дополнительных полей схемы:
fields = {}
for name, attr in IMember.namesAndDescriptions():
if isinstance(attr, schema.Field):
fields['visible_' + name] = schema.Bool(title=u'Display: ' + attr.title)
Теперь вы можете создать динамический интерфейс, подклассифицируя существующий интерфейс:
from zope.interface.interface import InterfaceClass
IMemberExtended = InterfaceClass('IMemberExtended', (IMember,), fields)
Все это можно обернуть в декоратор класса, если вы того пожелаете:
from zope.interface.interface import InterfaceClass
from zope import schema
def add_visibility_fields(iface):
fields = {}
for name, attr in iface.namesAndDescriptions():
if isinstance(attr, schema.Field):
fields['visible_' + name] = schema.Bool(title=u'Display: ' + attr.title)
return InterfaceClass(iface.__name__, (iface,), fields)
который вы бы использовали в вашем существующем интерфейсе:
@add_visibility_fields
class IMember(Interface):
first_name = schema.TextLine(title=u'first name')
last_name = schema.TextLine(title=u'last name')
Это создает подкласс; Вы также можете заменить весь интерфейс сгенерированным интерфейсом:
def add_visibility_fields(iface):
fields = {}
for name, attr in iface.namesAndDescriptions():
fields[name] = attr
if isinstance(attr, schema.Field):
fields['visible_' + name] = schema.Bool(title=u'Display: ' + attr.title)
return InterfaceClass(iface.__name__, iface.__bases__, fields)
Демо этой последней версии:
>>> @add_visibility_fields
... class IMember(Interface):
... first_name = schema.TextLine(title=u'first name')
... last_name = schema.TextLine(title=u'last name')
...
>>> IMember.names()
['visible_last_name', 'first_name', 'last_name', 'visible_first_name']
Похоже, вы хотите использовать метаклассы Python. Использование пользовательского метакласса позволит вам изменять создание класса, поэтому вы можете динамически добавлять или изменять атрибуты при создании класса или перед его созданием. Для превосходного ответа SO на метаклассах см. Этот пост.
Однако вы должны попытаться реструктурировать свою программу, чтобы избежать использования метаклассов. Обычно их следует использовать, только если вы действительно знаете, что они вам нужны. В вашем случае, возможно ли изменить schema.TextLine
разрешить поведение, которое вы хотите (возможно, добавив хук)?