Почему мое сообщение Protobuf (в Python) игнорирует нулевые значения?

Я работаю над внедрением protobufs для IPC для проекта. По какой-то причине значения, для которых установлено значение 0, не устанавливаются/не сериализуются. Для контекста файл .proto содержит следующее сообщение:

syntax = "proto3";

enum SetGet {
    SET = 0;
    GET = 1;
}

message State {
    SetGet setget = 1;
    double x = 2;
    double y = 3;
    double depth = 4;
    double yaw = 5;
    double pitch = 6;
    double roll = 7; 
}

Я компилирую файл в файл Python _pb2 с протоколом, а затем пытаюсь запустить следующий тестовый скрипт:

import filename_pb2 as pb

state = pb.State()
state.x = 0
state.y = 0
state.depth = 0
state.yaw = 0
state.pitch = 0
state.roll = 0
state.setget = pb.SET

print("State: {}".format(state))

state2 = pb.State()
state2.ParseFromString(state.SerializeToString())

print("State2: {}".format(state2))

Когда я запускаю его, печатается следующий вывод:

State: 
State2: 

Кажется, что ничего не устанавливается или нулевые значения каким-то образом игнорируются. Однако, когда я меняю значения (x, y, depth и т. д.) на что-то отличное от нуля, скажем, 0,1, я получаю следующий ожидаемый результат:

State: x: 0.1
y: 0.1
depth: 0.1
yaw: 0.1
pitch: 0.1
roll: 0.1

State2: x: 0.1
y: 0.1
depth: 0.1
yaw: 0.1
pitch: 0.1
roll: 0.1

Несмотря на то, что числа распечатаны, перечисление по какой-то причине все еще нет. Почему это происходит с protobufs? Является ли двойной тип 0 по умолчанию, поэтому сериализатор protobuf экономит место, игнорируя их? Почему же тогда они не восстанавливаются при разборе State2? Есть ли какая-то строка в документации, которую я пропустил? Заранее спасибо!

-- Тим


person Timothy Kanarsky    schedule 19.11.2017    source источник
comment
Если вы привыкли к proto2, то да: это ключевое изменение в proto3   -  person Marc Gravell    schedule 19.11.2017


Ответы (3)


Да, по умолчанию 0. Этот случай явно упоминается в документации:

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

person amalloy    schedule 19.11.2017
comment
Хорошо знать. Извините, что не уловил! - person Timothy Kanarsky; 19.11.2017

Ноль — это значение по умолчанию для числовых значений в protobuf, а пустые строки — значения по умолчанию для строк. Для эффективности значения по умолчанию не передаются по сети.

Если вы действительно хотите определить, было ли оно задано явно, не используйте для реальной операции значение по умолчанию:

enum SetGet {
    NONE = 0;
    SET = 1;
    GET = 2;
}

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

person paxdiablo    schedule 19.11.2017
comment
То, что вы говорите, безусловно, верно для proto3, но для proto2 и необработанного двоичного формата это сложнее. - person Marc Gravell; 19.11.2017

Это было изменено недавно; proto3 теперь поддерживает отслеживание присутствия, которое включается добавлением ключевого слова optional:

message State {
    optional SetGet setget = 1;
    optional double x = 2;
    optional double y = 3;
    optional double depth = 4;
    optional double yaw = 5;
    optional double pitch = 6;
    optional double roll = 7; 
}

из https://github.com/protocolbuffers/protobuf/blob/master/CHANGES.txt:

05.02.2021, версия 3.15.0 (C++/Java/Python/PHP/Objective-C/C#/Ruby/JavaScript)

Компилятор протоколов

  • Необязательные поля для proto3 включены по умолчанию и больше не требуют флага --experimental_allow_proto3_Optional.

который является RTM этого:

12 мая 2020 г., версия 3.12.0 (C++/Java/Python/PHP/Objective-C/C#/Ruby/JavaScript)

Компилятор протоколов

  • [экспериментальная] Отдельные поля, не являющиеся сообщениями, в proto3 теперь поддерживают отслеживание присутствия. Это можно сделать, добавив необязательную метку поля и передав флаг --experimental_allow_proto3_Optional в протокол.
person Marc Gravell    schedule 31.03.2021