为啥我的 Protobuf 消息(在 Python 中)忽略零值?
Posted
技术标签:
【中文标题】为啥我的 Protobuf 消息(在 Python 中)忽略零值?【英文标题】:Why is my Protobuf message (in Python) ignoring zero values?为什么我的 Protobuf 消息(在 Python 中)忽略零值? 【发布时间】:2017-11-19 05:23:42 【问题描述】:我一直致力于为一个项目实施 IPC 的 protobufs。出于某种原因,未设置/序列化设置为 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;
我使用 protoc 将文件编译为 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、深度等)更改为非零值(例如 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
即使数字被打印出来,由于某种原因,枚举仍然不是。 为什么 protobuf 会发生这种情况?默认情况下 double 类型为 0,因此 protobuf 序列化程序通过忽略它们来节省空间?那么,为什么在解析 State2 时它们没有被恢复呢?我错过了文档中的某些行吗?提前致谢!
-- 提姆
【问题讨论】:
如果你习惯了 proto2,那么是的:这是 proto3 的一个关键变化 【参考方案1】:是的,0 是默认值。这个案例在the documentation中明确提到:
请注意,对于标量消息字段,一旦解析了消息,就会有 无法判断一个字段是否被显式设置为默认值 值(例如布尔值是否设置为 false)或未设置 根本:在定义消息类型时应该牢记这一点。 例如,没有一个布尔值可以在以下情况下开启某些行为 如果您不希望该行为也发生,请设置为 false 默认。另请注意,如果标量消息字段设置为其 默认情况下,该值不会在线上序列化。
【讨论】:
很高兴知道。抱歉我没听清楚!【参考方案2】:零是protobuf中数字的默认值,空字符串是字符串的默认值。为了提高效率,默认值不会通过网络传输。
如果您真的想知道它是否被显式设置,请不要在实际操作中使用默认零:
enum SetGet
NONE = 0;
SET = 1;
GET = 2;
请记住,这会导致网络上的额外流量,并且由于您只是真正担心要打印的内容,您也可以只了解零是默认值,或者编写您的拥有输出所有内容的打印例程。
【讨论】:
你所说的对于 proto3 来说当然是正确的——但是对于 proto2 和原始二进制格式来说,它就更复杂了。【参考方案3】:最近已更改; 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:
2021-02-05 版本 3.15.0 (C++/Java/Python/php/Objective-C/C#/Ruby/javascript)
协议编译器
proto3 的可选字段默认启用,不再需要 --experimental_allow_proto3_optional 标志。
这是这个的RTM:
2020-05-12 版本 3.12.0 (C++/Java/Python/PHP/Objective-C/C#/Ruby/JavaScript)
协议编译器
[实验性] proto3 中的单数、非消息类型字段现在支持 存在跟踪。这是通过添加“可选”字段标签和 将 --experimental_allow_proto3_optional 标志传递给 protoc。
【讨论】:
为什么我们必须将该字段设置为可选以允许打印 0 值枚举?无论setget
字段是否可选,SetGet
枚举具有#0 的有效值都不会改变以上是关于为啥我的 Protobuf 消息(在 Python 中)忽略零值?的主要内容,如果未能解决你的问题,请参考以下文章
在 Google Cloud Build 中使用 python 插件编译 protobuf 消息
用于从流中读取多个 protobuf 消息的 python 示例