Python 比较

描述 Python 如何比较对象。

由于 proto 数据的序列化方式,您不能依赖 proto 消息实例的有线表示 (wire representation) 来确定其内容是否与另一个实例相同。有线格式的 proto 消息实例可能变化的方式包括但不限于以下几种:

  • protobuf schema 以某些方式发生变化。
  • map 字段以不同的顺序存储其值。
  • 二进制文件使用不同的标志(例如 opt vs. debug)构建。
  • protobuf 库已更新。

由于序列化数据可能以这些方式变化,确定相等性需要使用其他方法。

比较方法

您可以使用标准的 Python == 运算符来比较 protocol buffer 消息是否相等。使用 == 运算符比较两个对象时,会与 message.ListFields() 进行比较。在测试时,您可以使用 self.assertEqual(msg1, msg2)

如果两个消息具有相同的类型且它们所有对应的字段都相等,则认为它们相等。不等运算符 !=== 完全相反。

消息的相等性是递归的:要使两个消息相等,任何嵌套的消息也必须相等。

字段相等性和存在性

字段的相等性检查是基于值的。对于具有显式存在性的字段,字段的存在性也会被考虑在内。

一个被显式设置为其默认值的字段与一个未设置的字段被视为相等。

例如,考虑以下具有显式存在性字段的消息:

edition = "2023";
message MyMessage {
  int32 value = 1; // 'explicit' presence by default in Editions
}

如果您创建两个实例,一个实例中 value 未设置,另一个实例中 value 被显式设置为 0,那么它们将不相等:

msg1 = MyMessage()
msg2 = MyMessage()
msg2.value = 0

assert not msg1.HasField("value")
assert msg2.HasField("value")
assert msg1 != msg2

同样的原则也适用于子消息字段:一个未设置的子消息不等于一个存在但为空(子消息类的默认实例)的子消息。

对于具有隐式存在性的字段,由于无法跟踪其存在性,该字段总是通过其值与另一个消息中的对应字段进行比较。

这种将存在性作为相等性检查一部分的行为,与其他一些语言或 protobuf 库处理相等性的方式不同。在那些实现中,未设置的字段和设置为其默认值的字段有时被视为等效(通常是为了有线格式的兼容性)。在 Python 中,== 执行的是更严格的检查。

其他字段类型

  • 重复字段:如果元素数量相同且每个对应的元素都相等,则认为它们相等。元素的顺序很重要。
  • Map 字段:如果它们具有相同的键值对集合,则认为它们相等。键值对的顺序不重要。
  • 浮点字段floatdouble):按值进行比较。请注意浮点数比较中常见的注意事项。