Python protorpc dymnamic message

Я использую protorpc с endpoints-proto-datastore.

Я хочу создать собственное сообщение из предоставленной структуры.

Например, это следующий список ключей: ['id1', 'id2', 'id3']

Каждый ключ назначен MessageField названный CustomField,

Я бы хотел унаследовать от Message и класс, содержащий все key,

def create_custom_container(key_list):
    class cls():
        pass
    for i, k in enumerate(key_list):
        setattr(cls, k, MessageField(CustomField, i))
    return cls

class CustomMessage(Message, create_custom_container(key_list)):
    pass

Но это не работает, я получил:MessageDefinitionError: Message types may only inherit from Message

Я видел из исходного кода protorpc, что Message используйте метаклассы, чтобы предотвратить его наследование или иметь атрибуты, которые будут изменены на лету.

Итак, я понятия не имею, как создать свое собственное сообщение на лету.

1 ответ

Решение

Библиотека проходит долгий путь, определяя ограничения для класса Message - взлом его для принудительного ввода новых атрибутов, вероятно, приведет к тому, что Message будет работать не так, как ожидалось.

К счастью, вместо жесткого кодирования тела класса class CustomMessage оператор, создайте свой собственный класс с помощью вызова - это позволяет вам программно определять содержимое. Таким образом, вам не нужно использовать более одного класса в вашем дереве наследования.

Все, что вам нужно сделать, это позвонить Messageметакласс с соответствующими параметрами вместо обычного вызова typeи передать как пространство имен класса -

так что вы можете переписать вашу функцию создания тела:

def create_custom_body(key_list):
    dct = {}
    for i, k in enumerate(key_list):
        dct[k] = MessageField(CustomField, i)
    return dct

CustomClass  = Message.__class__("CustomClass", (Message,), create_custom_body(key_list))

Это будет работать в этом случае. Если метакласс библиотеки будет использовать пользовательское пространство имен (то есть он будет иметь __prepare__ метод), однако, вам нужно изменить это, чтобы использовать types.new_class и соответствующий обратный вызов:

from types import new_class

def create_custom_body(dct, key_list):
    for i, k in enumerate(key_list):
        dct[k] = MessageField(CustomField, i)
    return dct

CustomClass  = types.new_class(
    "CustomClass", (Message,), 
    exec_body=(lambda namespace: create_custom_body(namespace, key_list))
)

(проверьте документы по адресу: https://docs.python.org/3/library/types.html).

Другие вопросы по тегам