Связывание свойств с объектами класса - PullRequest
2 голосов
/ 29 августа 2010

Я хотел бы определить свойства для классов и иметь возможность получить к ним доступ, прежде чем я на самом деле создаю объект этого класса.

Я приведу некоторый контекст.Мое приложение обрабатывает библиотеку компонентов.Каждый компонент сопоставлен с классом Python.Теперь я хочу знать, какая конфигурация нужна компоненту, прежде чем создавать экземпляр класса.

Одно из решений - написать что-то вроде этого:

class Component:
    @classmethod
    def config(cls, name, description, default=None):
        ''' Define one configuration switch for the class. '''
        # now put this information in a class-specific dictionary

class Model1(Component):
    @classmethod
    def define_configuration(cls):
        cls.config('n', 'number of burzs to instigate')
        cls.config('skip', 'skip any barzs among the burzs', default=True)
    # ...

component_class = Model1
component_class.define_configuration()

Однако это выглядит довольно уродливо.В идеале я хотел бы иметь возможность написать что-то вроде следующего, и все же иметь возможность помещать переключатели конфигурации в словарь для конкретного класса, к которому можно будет обращаться позже.

class Model1(Component):
    config('n', 'number of burz to instigate')
    config('skip', 'skip any barz in the data', default=True)

Моим первоначальным решением было написать что-тонапример:

class Model1(Component):
    Model1.config('n', 'number of burz to instigate')
    Model1.config('skip', 'skip any barz in the data', default=True)

однако я обнаружил здесь и другие вопросы о SO, что имя класса еще не определено при выполнении тела.

Что мне делать?

tl; dr: Как мне получить хороший синтаксис для определения свойств, специфичных для класса (перед тем, как я создаю экземпляр объекта этого класса)?


Вот (для записи)решение, которое было предложено (немного разработано).Яй!Я мог получить именно то, что хотел.: -)

from collections import namedtuple
Config = namedtuple('Config', 'name desc default')

def config(name, description, default=None):
    ComponentMeta.tmp_config_storage.append(Config(name, description, default))

class ComponentMeta(type):
    tmp_config_storage = []
    def __init__(cls, clsname, bases, clsdict):
        for config in ComponentMeta.tmp_config_storage:
            if not 'my_config' in cls.__dict__:
                setattr(cls, 'my_config', [])
            cls.my_config.append(config)
        ComponentMeta.tmp_config_storage = []

class Component(object):
    __metaclass__ = ComponentMeta

class Model1(Component):
    config('x1', 'for model1')
    config('y1', 'for model1')

class Model2(Component):
    config('x2', 'for model2')

print 'config for Model1:', Model1.my_config
print 'config for Model2:', Model2.my_config

Ответы [ 3 ]

1 голос
/ 29 августа 2010

исправлена ​​

def config(name, description, default=None):
    ComponentMeta.config_items.append((name, description, default))

class ComponentMeta(type):
    config_items = []
    def __init__(cls, clsname, bases, clsdict):
        for options in ComponentMeta.config_items:
                cls.add_config(*options)
        ComponentMeta.config_items = []

class Component(object):
    __metaclass__ = ComponentMeta
    config_items = [] # this is only for testing. you don't need it
    @classmethod
    def add_config(cls, name, description, default=None):
        #also for testing
        cls.config_items.append((name, description, default))

class Model1(Component):
    config('n', 'number of burz to instigate')
    config('skip', 'skip any barz in the data', default=True)

print Model1.config_items

Это работает, делая config внешней функцией, которая добавляет элементы в ComponentMeta.config_instances. ComponentMeta затем проверяет этот список при создании класса и вызывает config_item для элементов. Обратите внимание, что это не потокобезопасно (хотя это может быть сделано мной). Кроме того, если подкласс ComponentMeta не может вызвать super или сам пуст ComponentMeta.config_items, следующий созданный класс получит элементы конфигурации.

1 голос
/ 29 августа 2010

Почему бы не сделать что-то простое, скажем:

def makeconfigdict(cls):
    thedict = {}
    for name, value in cls.__dict__.items():
       if isaconfig(value):
           addconfigtodict(thedict, name, value)
           delattr(cls, name)
    cls.specialdict = thedict
    return cls

@makeconfigdict
class Model1(Component):
    n = config('number of burz to instigate')
    skip = config('skip any barz in the data', default=True)
    ...

Пока функция config возвращает объекты, которые функция isaconfig может распознать, и функция addconfigtodict может быть правильно установлена ​​вспециальный словарь в любом формате, который вы хотите, вы в клевер.

Если я правильно понимаю ваши спецификации, вы не хотите нормальный атрибут, такой как Model1.skip, верно?Вот почему у меня есть delattr вызов в makeconfigdict (а также почему я использую .items() в словаре класса - поскольку словарь изменяется во время цикла, лучше использовать .items(), который делает "снимок""список всех имен и значений, а не обычный .iteritems(), который просто повторяется в словаре, таким образом не , что позволяет изменять его во время цикла).

1 голос
/ 29 августа 2010

Имя класса определяется после выполнения тела.Это происходит, когда импортируется файл, содержащий класс.Это не то же самое, что создание экземпляра класса.

class A(object):
    """ doc """
    config = []
    def __init__(def):
        pass
A.config.append(('n', 'number of burz to instigate'))
A.config.append(('skip', 'skip any barz in the data', default=True))
# access class specific config before object instantiation
x = A.config[0]
# instantiating your class, post configuration access
a_obj = A()

Не подходит ли этот подход вашей цели?Конфигурация класса может быть сохранена в переменной класса, которую можно изменить и добавить до создания экземпляров любых объектов этого класса.

Использование переменных класса должно служить вашей цели.

...