【问题标题】:How do I assign a oneof field on a protobuf message if the child message has no fields?如果子消息没有字段,如何在 protobuf 消息上分配 oneof 字段?
【发布时间】:2026-01-26 04:25:05
【问题描述】:

我想创建一个 BigTable DeleteFromRow 突变。 MutationDeleteFromRow 的原型如下所示:

oneof mutation {
    // Set a cell's value.
    SetCell set_cell = 1;

    // Deletes cells from a column.
    DeleteFromColumn delete_from_column = 2;

    // Deletes cells from a column family.
    DeleteFromFamily delete_from_family = 3;

    // Deletes cells from the entire row.
    DeleteFromRow delete_from_row = 4;
  }
}

message DeleteFromRow {

}

在 Python 中,您不能直接实例化 DeleteFromRow 对象并将 Mutationdelete_from_row 字段设置为该对象。

所以这行不通

request = bigtable_pb2.MutateRowRequest(table_name='tablename', row_key=row_key)
mutation = request.mutations.add()
mutation.delete_from_row = data_pb2.Mutation.DeleteFromRow()

正如其他 SO 用户提出的(请参阅 this question),这会导致

AttributeError: Assignment not allowed to composite field "delete_from_row" in protocol message object.

根据protobuf docs,您应该通过设置一个子字段来设置一个oneof字段。所以应该这样创建一个DeleteFromFamily 突变:

mutation.delete_from_family.family_name = 'some_family'

但是,对于没有字段的DeleteFromRow 消息,我该如何处理?

【问题讨论】:

  • 出于好奇,你为什么不用官方客户端?
  • 因为它不完整。它是这个 grpc 实现之上的抽象。特别是,当我想根据一组行键或一组行键前缀读取行时,我切换到允许您在单个请求中执行此操作的底层 grpc 客户端。
  • 知道了。 FWIW,我们现在正在开发该功能。

标签: python grpc bigtable


【解决方案1】:

你可以使用Message.SetInParent:

将此标记为存在于父级中。

当您分配子消息的字段时,这通常会自动发生,但有时您希望在保留子消息的同时将其保留为空。如果您发现自己在使用它,您可能需要重新考虑您的设计。

例子:

message Msg {
  oneof kind {
    int64 int_field = 1;
    EmptyMsg msg_field = 1;
  }
}

message EmptyMsg {}
msg = Msg()
print(msg.WhichOneof('kind'))  # None

msg.msg_field  # No-op (return EmptyMsg but don't set oneof field)
print(msg.WhichOneof('kind'))  # None

msg.msg_field.SetInParent()
print(v.WhichOneof('kind'))  # msg_field

【讨论】:

    【解决方案2】:

    您可以启动DeleteFromRow 对象并使用关键字参数delete_from_row 创建一个突变:

    dfr = data_pb2.Mutation.DeleteFromRow()
    mutation = data_pb2.Mutation(delete_from_row=dfr)
    

    虽然您不能 addappend 对请求的重复突变字段进行这种突变(尽管在我看来这是文档所说的 here),但您可以 extend它:

    request = bigtable_pb2.MutateRowRequest(table_name='tablename', row_key=row_key)
    request.mutations.extend([mutation])
    

    【讨论】:

    • 关于该文档的链接:Repeated FieldRepeated Message Field 之间存在差异。后者没有 append 功能 - 如文档所述。