Как автоматически генерировать свойства в метаклассе?

Как сгенерировать свойства в python с помощью метакласса? У меня есть некоторые записи данных, поля которых имеют некоторые отношения между ними. и я хотел бы, чтобы каждая запись была типом (классом) и автоматически генерировала эти свойства и отношения.

Я хочу указать это отношение как структуру данных (скажем, dict) и автоматически генерировать классы со свойствами

Record 1
        Field        description
       ----------------------------
        numOfPin      no# of following pin array
        pin_array     array with numOfpin elements
        read_result   if opt_flag bit 0 is set, invalid. ignore this value
        opt_flag       ...
Record 2
   ....

Изменить: для уточнения. Логика свойств для отсутствующего/недопустимого поля и необязательного флага одинакова для всех записей. Поэтому я хотел бы абстрагироваться от этого в метаклассе.

например, в псевдо-коде Python:

record1_spec = { 'numOfpin', ('pin_array','numOfPin'),
                 'opt_flag', ('read_result','opt_flag') }
record2_spec = { 'numOfsite', ('site_array','numOfsite'),
                 'opt_flag', ('test_result','opt_flag') }

class MetaClass:
    getter_generator( ele, opt ):
       if opt : return ele
       else return None

    get class name (eg. record1) then fetch record_spec to create class
    for ele in record_spec:
       if ele is tuple type:   # relation between field
           class.attribute[ele[0]] = getter_generator(ele[0], ele[1])


class record1(metaclass = Metaclass):
    ...

Тогда класс record1 будет иметь все свойства, определенные в record1_spec. Логика заключается в том, что если какое-либо поле параметра установлено, некоторые свойства возвращают соответствующее значение.


person Nyan    schedule 11.08.2015    source источник
comment
Немного неясно, что вы пытаетесь сделать и нужен ли вам вообще метакласс для ваших нужд. Например, как вы собираетесь присваивать значения поля? Обратите внимание, что метакласс определяет, как создается сам класс, а не то, как определяются экземпляры класса.   -  person skyking    schedule 11.08.2015
comment
@skyking Я хочу, чтобы метакласс создавал классы (для каждого типа записи, а не для объекта записи!), И каждая запись type должна иметь некоторые свойства, и эти свойства также могут быть сгенерированы метаклассом. У меня есть основная идея. но все онлайн-руководства/учебники по метаклассам python не очень полезны.   -  person Nyan    schedule 11.08.2015
comment
Не могли бы вы привести пример того, как вы хотели бы использовать это?   -  person skyking    schedule 11.08.2015
comment
Пожалуйста, отредактируйте свой вопрос и добавьте описание, показывающее класс, который будет создан оператором class record1(metaclass = Metaclass):. Мне неясно, какие именно свойства вы хотите автоматически сгенерировать и добавить к нему (а также какие именно действия они выполняют).   -  person martineau    schedule 19.07.2018


Ответы (1)


Ладно, посмотрим, насколько глубока кроличья нора...

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

def make_property(params):
    def fn(self):
         return use_self_and_params_to_calculate_result(self, params)
    return property(fn)

например, params может, например, быть именем поля, а затем вы можете вернуть его, просто выполнив return getattr(self, fieldname) во внутренней (т.е. fn) функции.

Теперь для следующей части, создания класса. Параметр metaclass при создании класса используется непосредственно только для этого - он не обязательно должен быть фактическим метаклассом, он просто вызывается и должен возвращать что-то, что считается классом. Обычный вариант использования, однако, состоит в том, чтобы параметр metaclass был типом класса, но на самом деле нам это не нужно в этом случае:

def meta(name, bases, attrs):
    attrs = dict(attrs)

    attrs["x"] = make_property("_x")

    return type(name, bases, attrs)

class record1(metaclass=meta):
    pass

Теперь мы могли искать record1_spec в meta через globals() (meta вызывается с "record1" в качестве первого аргумента), но что в этом интересного. Давайте копнем глубже в кроличью дыру, meta просто вызывается с тем, что вы поместите туда, где идут базовые классы - просто так получилось, что type ожидает, что они будут классами, но нам не нужно этого делать, мы только должны убедиться, что это правда, когда мы звоним type:

def meta(name, bases, attrs):
    record_spec = bases[0]
    bases = bases[1:]

    attrs = dict(attrs)

    examine_record_spec_and_populate_attrs(attrs, record_spec)
    attrs["spec"] = record_spec

    return type(name, bases, attrs)

class record1(['numOfpin', ('pin_array','numOfPin'), 
              'opt_flag', ('read_result','opt_flag')], metaclass=meta):
    pass

Затем вы можете найти спецификацию как record1.spec, если вам это нужно. Если ваша спецификация записи находится в парах ключ-значение (например, как dict), вы даже можете использовать параметры ключевого слова в определении класса:

 def meta(name, bases, attrs, **spec):
    attrs = dict(attrs)

    examine_record_spec_and_populate_attrs(attrs, spec)
    attrs["spec"] = spec

    return type(name, bases, attrs)

class record1(numOfpin = ('pin_array','numOfPin'), 
              opt_flag = ('read_result','opt_flag'), metaclass=meta):
    pass
person skyking    schedule 11.08.2015