Protobuf メッセージ

use_proto_plus 構成パラメータを使用すると、ライブラリで proto-plus メッセージと protobuf メッセージのどちらを返すかを指定できます。このパラメータの設定方法については、構成ドキュメントをご覧ください。

このセクションでは、各オプションのパフォーマンスへの影響について説明します。これにより、アプリケーションに最適なアプローチを選択できます。

proto-plus メッセージと protobuf メッセージ

コード生成ツール パイプラインは、protobuf メッセージ インターフェースの人間工学を改善するために、proto-plus を統合して、標準の Python オブジェクトのように動作するようにします。ただし、proto-plus を使用すると、パフォーマンス オーバーヘッドが発生します。

proto-plus のパフォーマンス

proto-plus の主な特典の 1 つは、型マーシャリングと呼ばれるプロセスを通じて、protobuf メッセージと既知の型を組み込みの Python 型に変換することです。

マーシャリングは、proto-plus メッセージ インスタンスでフィールドがアクセスされたとき、具体的には、protobuf 定義でフィールドが読み取られたとき、または設定されたときに発生します。

syntax = "proto3";

message Dog {
  string name = 1;
}

この定義を proto-plus クラスに変換すると、次のようになります。

import proto


class Dog(proto.Message):
    name = proto.Field(proto.STRING, number=1)

次に、Dog クラスを初期化し、他の Python オブジェクトと同様に name フィールドにアクセスできます。

dog = Dog()
dog.name = "Scruffy"
print(dog.name)

name フィールドの読み取りと設定を行う際、値は組み込みの Python str 型から string 型に変換され、protobuf ランタイムと互換性のある値になります。

この型の変換にかかる時間は、パフォーマンスに大きな影響を与えるため、アプリケーションのニーズに基づいて、proto-plus メッセージと protobuf メッセージのどちらを使用するかを決定する必要があります。

proto-plus メッセージと protobuf メッセージのユースケース

Proto-plus メッセージのユースケース
Proto-plus は、protobuf メッセージに比べて人間工学的な改善が数多く施されているため、保守性と読みやすさに優れたコードの作成に最適です。標準の Python オブジェクトを公開するため、使いやすく理解しやすくなっています。
Protobuf メッセージのユースケース
パフォーマンスが重要なユースケース、特に大量のレポートを迅速に処理する必要があるアプリや、多数のオペレーションを含む変更リクエストを構築するアプリ(BatchJobService や OfflineUserDataJobService など)では、protobuf を使用します。

メッセージ タイプを動的に切り替える

アプリに適したメッセージ タイプを選択した後、特定のワークフローで別のタイプを使用する必要があることが判明する場合があります。この場合、クライアント ライブラリが提供するユーティリティを使用して、2 つの型を動的に切り替えることができます。前の例と同じ Dog メッセージ クラスを使用します。

from google.ads.googleads import util

# Proto-plus message type
dog = Dog()

# Protobuf message type
dog = util.convert_proto_plus_to_protobuf(dog)

# Back to proto-plus message type
dog = util.convert_protobuf_to_proto_plus(dog)

Protobuf メッセージ インターフェースの違い

proto-plus インターフェースについては、詳細なドキュメントが用意されています。次のセクションでは、Google Ads クライアント ライブラリの一般的なユースケースに影響する主な違いについて説明します。

バイトのシリアル化

proto-plus メッセージ
serialized = type(campaign).serialize(campaign)
deserialized = type(campaign).deserialize(serialized)
Protobuf メッセージ
serialized = campaign.SerializeToString()
deserialized = campaign.FromString(serialized)

JSON シリアル化

proto-plus メッセージ
serialized = type(campaign).to_json(campaign)
deserialized = type(campaign).from_json(serialized)
Protobuf メッセージ
from google.protobuf.json_format import MessageToJson, Parse

serialized = MessageToJson(campaign)
deserialized = Parse(serialized, campaign)

フィールド マスク

api-core によって提供されるフィールド マスク ヘルパー メソッドは、protobuf メッセージ インスタンスを使用するように設計されています。proto-plus メッセージを使用する場合は、ヘルパーを使用するために protobuf メッセージに変換します。

proto-plus メッセージ
from google.api_core.protobuf_helpers import field_mask

campaign = client.get_type("Campaign")
protobuf_campaign = util.convert_proto_plus_to_protobuf(campaign)
mask = field_mask(None, protobuf_campaign)
Protobuf メッセージ
from google.api_core.protobuf_helpers import field_mask

campaign = client.get_type("Campaign")
mask = field_mask(None, campaign)

列挙型

proto-plus メッセージによって公開される列挙型は、Python の組み込み enum 型のインスタンスであるため、多くの便利なメソッドを継承します。

列挙型の取得

GoogleAdsClient.get_type メソッドを使用して列挙型を取得する場合、返されるメッセージは、proto-plus メッセージを使用しているか protobuf メッセージを使用しているかによって若干異なります。次に例を示します。

proto-plus メッセージ
val = client.get_type("CampaignStatusEnum").CampaignStatus.PAUSED
Protobuf メッセージ
val = client.get_type("CampaignStatusEnum").PAUSED

列挙型の取得を簡単にするため、使用しているメッセージ タイプに関係なく一貫したインターフェースを持つ便利な属性が GoogleAdsClient インスタンスにあります。

val = client.enums.CampaignStatusEnum.PAUSED

列挙値の取得

特定の列挙型の値またはフィールド ID を知っておくと便利な場合があります。たとえば、CampaignStatusEnum の PAUSED は 3 に対応します。

proto-plus メッセージ
campaign = client.get_type("Campaign")
campaign.status = client.enums.CampaignStatusEnum.PAUSED
# To read the value of campaign status
print(campaign.status.value)
Protobuf メッセージ
campaign = client.get_type("Campaign")
status_enum = client.enums.CampaignStatusEnum
campaign.status = status_enum.PAUSED
# Native protobuf enum fields already store the integer value (3):
print(campaign.status)
# Or look up the integer value from the enum name string:
print(status_enum.CampaignStatus.Value("PAUSED"))

列挙型の名前の取得

列挙型フィールドの名前を知っておくと便利な場合があります。たとえば、API からオブジェクトを読み取るときに、整数 3 がどのキャンペーン ステータスに対応しているかを知りたい場合があります。

proto-plus メッセージ
campaign = client.get_type("Campaign")
campaign.status = client.enums.CampaignStatusEnum.PAUSED
# To read the name of campaign status
print(campaign.status.name)
Protobuf メッセージ
campaign = client.get_type("Campaign")
status_enum = client.enums.CampaignStatusEnum
# Sets the campaign status to the int value for PAUSED
campaign.status = status_enum.PAUSED
# To read the name of campaign status
print(status_enum.CampaignStatus.Name(campaign.status))

繰り返しフィールド

proto-plus ドキュメントで説明されているように、繰り返しフィールドは通常、型付きリストと同等です。つまり、list とほぼ同じように動作します。

繰り返しスカラー フィールドに値を追加する

string フィールドや int64 フィールドなどの繰り返しスカラー型フィールドに値を追加する場合、メッセージ タイプに関係なくインターフェースは同じです。

proto-plus メッセージ
ad.final_urls.append("https://www.example.com")
Protobuf メッセージ
ad.final_urls.append("https://www.example.com")

これには、他の一般的な list メソッド(extend など)も含まれます。

proto-plus メッセージ
ad.final_urls.extend(
    ["https://www.example.com", "https://www.example.com/2"]
)
Protobuf メッセージ
ad.final_urls.extend(
    ["https://www.example.com", "https://www.example.com/2"]
)

メッセージ タイプを繰り返しフィールドに追加する

繰り返しフィールドがスカラー型でない場合、繰り返しフィールドに追加する際の動作は若干異なります。

proto-plus メッセージ
frequency_cap = client.get_type("FrequencyCapEntry")
frequency_cap.cap = 100
campaign.frequency_caps.append(frequency_cap)
Protobuf メッセージ
# The add method initializes a message and adds it to the repeated field
frequency_cap = campaign.frequency_caps.add()
frequency_cap.cap = 100

繰り返しフィールドを割り当てる

スカラーと非スカラーの両方の繰り返しフィールドで、さまざまな方法でフィールドにリストを割り当てることができます。

proto-plus メッセージ
# In proto-plus it's possible to use assignment.
urls = ["https://www.example.com"]
ad.final_urls = urls
Protobuf メッセージ
# Protobuf messages do not allow assignment, but you can replace the
# existing list using slice syntax.
urls = ["https://www.example.com"]
ad.final_urls[:] = urls

空のメッセージ

メッセージ インスタンスに情報が含まれているか、フィールドが設定されているかを知ることが役立つ場合があります。

proto-plus メッセージ
# When using proto-plus messages you can check the message for truthiness.
is_empty = not bool(campaign)
Protobuf メッセージ
is_empty = campaign.ByteSize() == 0

メッセージのコピー

proto-plus メッセージと protobuf メッセージの両方で、GoogleAdsClient の copy_from ヘルパー メソッドを使用します。

client.copy_from(campaign, other_campaign)

メッセージ フィールドが空

空のメッセージ フィールドを設定するプロセスは、使用しているメッセージ タイプに関係なく同じです。問題のフィールドに空のメッセージをコピーします。メッセージのコピーのセクションと、メッセージ フィールドを空にするガイドもご覧ください。次の例は、空のメッセージ フィールドを設定する方法を示しています。

client.copy_from(campaign.manual_cpm, client.get_type("ManualCpm"))

予約語であるフィールド名

proto-plus メッセージを使用する場合、名前が Python の予約語でもあると、フィールド名に末尾のアンダースコアが自動的に付加されます。次の例は、Asset インスタンスを操作する方法を示しています。

asset = client.get_type("Asset")
asset.type_ = client.enums.AssetTypeEnum.IMAGE

予約済みの名前の完全なリストは、gapic ジェネレータ モジュールで作成されます。プログラムでアクセスすることもできます。

まず、モジュールをインストールします。

python -m pip install gapic-generator

次に、Python REPL またはスクリプトで次の操作を行います。

import gapic.utils

print(gapic.utils.reserved_names.RESERVED_NAMES)

フィールド プレゼンス

protobuf メッセージ インスタンスのフィールドにはデフォルト値があるため、フィールドが設定されているかどうかを直感的に把握できるとは限りません。

proto-plus メッセージ
# Use the "in" operator.
has_field = "name" in campaign
Protobuf メッセージ
campaign = client.get_type("Campaign")
# Determines whether "name" is set and not just an empty string.
campaign.HasField("name")

protobuf の Message クラス インターフェースには、メッセージのサブメッセージ、oneof フィールド、optional スカラー フィールドがデフォルト値に設定されている場合でも、設定されているかどうかを判断する HasField メソッドがあります。(proto3 のオプション以外のスカラー フィールドで HasField を呼び出すと、ValueError が発生します)。

Protobuf メッセージ メソッド

protobuf メッセージ インターフェースには、proto-plus インターフェースの一部ではない便利なメソッドが含まれていますが、proto-plus メッセージを protobuf の対応するメッセージに変換することでアクセスできます。

# Accessing the ListFields method
protobuf_campaign = util.convert_proto_plus_to_protobuf(campaign)
print(protobuf_campaign.ListFields())

# Accessing the Clear method
protobuf_campaign = util.convert_proto_plus_to_protobuf(campaign)
protobuf_campaign.Clear()

公開バグトラッカー

これらの変更についてご不明な点がある場合や、ライブラリの最新バージョンへの移行で問題が発生した場合は、Issue Tracker で問題を報告してください。