|
| 1 | ++++ |
| 2 | +title = "Python Comparison" |
| 3 | +weight = 751 |
| 4 | +linkTitle = "Python Comparison" |
| 5 | +description = "Describes how Python compares objects." |
| 6 | +type = "docs" |
| 7 | ++++ |
| 8 | + |
| 9 | +Because of how proto data is serialized, you cannot rely on the wire |
| 10 | +representation of a proto message instance to determine if its content is the |
| 11 | +same as another instance. A subset of the ways that a wire-format proto message |
| 12 | +instance can vary include the following: |
| 13 | + |
| 14 | +* The protobuf schema changes in certain ways. |
| 15 | +* A map field stores its values in a different order. |
| 16 | +* The binary is built with different flags (such as opt vs. debug). |
| 17 | +* The protobuf library is updated. |
| 18 | + |
| 19 | +Because of these ways that serialized data can vary, determining equality |
| 20 | +involves other methods. |
| 21 | + |
| 22 | +## Comparison Methods {#methods} |
| 23 | + |
| 24 | +You can compare protocol buffer messages for equality using the standard Python |
| 25 | +`==` operator. Comparing two objects using the `==` operator compares with |
| 26 | +`message.ListFields()`. When testing, you can use `self.assertEqual(msg1, |
| 27 | +msg2)`. |
| 28 | + |
| 29 | +Two messages are considered equal if they have the same type and all of their |
| 30 | +corresponding fields are equal. The inequality operator `!=` is the exact |
| 31 | +inverse of `==`. |
| 32 | + |
| 33 | +Message equality is recursive: for two messages to be equal, any nested messages |
| 34 | +must also be equal. |
| 35 | + |
| 36 | +## Field Equality and Presence |
| 37 | + |
| 38 | +The equality check for fields is value-based. For fields with |
| 39 | +[explicit presence](#singular-explicit), the presence of a field is also taken |
| 40 | +into account. |
| 41 | + |
| 42 | +A field that is explicitly set to its default value is **not** considered equal |
| 43 | +to a field that is unset. |
| 44 | + |
| 45 | +For example, consider the following message which has an explicit presence |
| 46 | +field: |
| 47 | + |
| 48 | +```proto |
| 49 | +edition = "2023"; |
| 50 | +message MyMessage { |
| 51 | + int32 value = 1; // 'explicit' presence by default in Editions |
| 52 | +} |
| 53 | +``` |
| 54 | + |
| 55 | +If you create two instances, one where `value` is unset and one where `value` is |
| 56 | +explicitly set to `0`, they will not be equal: |
| 57 | + |
| 58 | +```python |
| 59 | +msg1 = MyMessage() |
| 60 | +msg2 = MyMessage() |
| 61 | +msg2.value = 0 |
| 62 | + |
| 63 | +assert not msg1.HasField("value") |
| 64 | +assert msg2.HasField("value") |
| 65 | +assert msg1 != msg2 |
| 66 | +``` |
| 67 | + |
| 68 | +This same principle applies to sub-message fields: an unset sub-message is not |
| 69 | +equal to a sub-message that is present but empty (a default instance of the |
| 70 | +sub-message class). |
| 71 | + |
| 72 | +For fields with [implicit presence](#singular-implicit), since presence cannot |
| 73 | +be tracked, the field is always compared by its value against the corresponding |
| 74 | +field in the other message. |
| 75 | + |
| 76 | +This behavior, where presence is part of the equality check, is different from |
| 77 | +how some other languages or protobuf libraries might handle equality, where |
| 78 | +unset fields and fields set to their default value are sometimes treated as |
| 79 | +equivalent (often for wire-format compatibility). In Python, `==` performs a |
| 80 | +stricter check. |
| 81 | + |
| 82 | +## Other Field Types {#other-types} |
| 83 | + |
| 84 | +* **Repeated fields** are equal if they have the same number of elements and |
| 85 | + each corresponding element is equal. The order of elements matters. |
| 86 | +* **Map fields** are equal if they have the same set of key-value pairs. The |
| 87 | + order of pairs does not matter. |
| 88 | +* **Floating-point fields** (`float` and `double`) are compared by value. Be |
| 89 | + aware of the usual caveats with floating-point comparisons. |
0 commit comments