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

Как да генерирам свойства в 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
   ....

Редактиране: за изясняване.. Логиката за свойства за липсващо/невалидно поле и незадължителен флаг е една и съща за всички записи. Така че бих искал да абстрахирам това в мета-клас.

например в код на псевдо питон:

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 Искам да направя метаклас, за да създавам класове (за всеки тип запис. не обект на запис!) и всеки тип запис ще трябва да има някои свойства и тези свойства могат също да бъдат генерирани от метаклас. Имам основната идея. но всички онлайн ръководства/уроци за метаклас на 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